diff --git a/Cargo.lock b/Cargo.lock index 3244b7c..9c2261c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,33 +1,33 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "bpaf" -version = "0.9.9" +version = "0.9.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc932b40b31d9bea0196f54c5b1b721b9aff3882fc08d9fe4082970c7e94d3d" +checksum = "0b86829876e7e200161a5aa6ea688d46c32d64d70ee3100127790dde84688d6e" dependencies = [ "bpaf_derive", "owo-colors", @@ -36,9 +36,9 @@ dependencies = [ [[package]] name = "bpaf_derive" -version = "0.5.7" +version = "0.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efeab2975f8102de445dcf898856a638332403c50216144653a89aec22fd79e0" +checksum = "2f7e98cee839b19076cb3ce1afdb62bb182e04ff5f71f70188827002fae91094" dependencies = [ "proc-macro2", "quote", @@ -47,32 +47,31 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytes" -version = "1.5.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "camino" -version = "1.1.6" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "cargo-hackerman" -version = "0.2.9" +version = "0.2.10" dependencies = [ "anyhow", "bpaf", - "cargo-platform", "cargo_metadata", "dot", "pathdiff", @@ -90,18 +89,19 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.6" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" +checksum = "dd0061da739915fae12ea00e16397555ed4371a6bb285431aab930f61b0aa4ba" dependencies = [ "serde", + "serde_core", ] [[package]] name = "cargo_metadata" -version = "0.18.1" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" dependencies = [ "camino", "cargo-platform", @@ -111,17 +111,11 @@ dependencies = [ "thiserror", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfg-expr" -version = "0.15.6" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6100bc57b6209840798d95cb2775684849d332f7bd788db2a8c8caf7ef82a41a" +checksum = "fb693542bcafa528e198be0ebd9d3632ca5b7c93dbe7237460e199910835997c" dependencies = [ "smallvec", "target-lexicon", @@ -129,15 +123,15 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "memchr", @@ -145,9 +139,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.4" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -155,9 +149,20 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "displaydoc" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "dot" @@ -167,41 +172,84 @@ checksum = "a74b6c4d4a1cff5f454164363c16b72fa12463ca6b31f4b5f2035a65fa3d5906" [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "fastrand" -version = "2.0.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fixedbitset" -version = "0.4.2" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + [[package]] name = "guppy-workspace-hack" version = "0.1.0" @@ -210,54 +258,144 @@ checksum = "92620684d99f750bae383ecb3be3748142d6095760afd5cbcf2261e9a279d780" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "icu_collections" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] [[package]] -name = "hermit-abi" -version = "0.3.4" +name = "icu_locale_core" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] [[package]] -name = "home" -version = "0.5.9" +name = "icu_normalizer" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ - "windows-sys 0.52.0", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", ] +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "idna" -version = "0.5.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", ] [[package]] -name = "indexmap" -version = "2.2.2" +name = "idna_adapter" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ - "equivalent", - "hashbrown", + "icu_normalizer", + "icu_properties", ] [[package]] -name = "is-terminal" -version = "0.4.10" +name = "indexmap" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.52.0", + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", ] [[package]] @@ -268,88 +406,120 @@ checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jni" -version = "0.21.1" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" dependencies = [ - "cesu8", "cfg-if", "combine", + "jni-macros", "jni-sys", "log", + "simd_cesu8", "thiserror", "walkdir", - "windows-sys 0.45.0", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn", ] [[package]] name = "jni-sys" -version = "0.3.0" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" dependencies = [ + "cfg-if", + "futures-util", "wasm-bindgen", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] -name = "log" -version = "0.4.20" +name = "litemap" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] -name = "malloc_buf" -version = "0.0.6" +name = "log" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] name = "memchr" -version = "2.7.1" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "ndk-context" @@ -358,143 +528,162 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] -name = "objc" -version = "0.2.7" +name = "objc2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ - "malloc_buf", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "objc2", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "owo-colors" -version = "4.0.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" dependencies = [ "camino", ] [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "petgraph" -version = "0.6.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", + "hashbrown 0.15.5", "indexmap", + "serde", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] -name = "proc-macro2" -version = "1.0.78" +name = "potential_utf" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ - "unicode-ident", + "zerovec", ] [[package]] -name = "quote" -version = "1.0.35" +name = "prettyplease" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", + "syn", ] [[package]] -name = "raw-window-handle" -version = "0.5.2" +name = "proc-macro2" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] [[package]] -name = "regex" -version = "1.10.3" +name = "quote" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.5", - "regex-syntax 0.8.2", + "proc-macro2", ] [[package]] -name = "regex-automata" -version = "0.1.10" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] -name = "regex-syntax" -version = "0.8.2" +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] [[package]] name = "rustix" -version = "0.38.31" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] -name = "ryu" -version = "1.0.16" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "same-file" @@ -507,27 +696,38 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.21" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" dependencies = [ "serde", + "serde_core", ] [[package]] name = "serde" -version = "1.0.196" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -536,13 +736,24 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", - "ryu", + "memchr", "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", ] [[package]] @@ -554,77 +765,115 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + [[package]] name = "smallvec" -version = "1.13.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "supports-color" -version = "2.1.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" +checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" dependencies = [ - "is-terminal", "is_ci", ] [[package]] name = "syn" -version = "2.0.48" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "target-lexicon" -version = "0.12.13" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" [[package]] name = "target-spec" -version = "3.1.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a8e795b1824524d13cdf04f73cf8b4f244ce86c96b4d2a83a6ca1a753d2752" +checksum = "b00e973676af5497c2a69cc9787e2205c00f3b6f4f70e7d7b0112e28aa84b501" dependencies = [ "cfg-expr", "guppy-workspace-hack", "target-lexicon", - "unicode-ident", ] [[package]] name = "tempfile" -version = "3.10.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "cfg-if", "fastrand", + "getrandom", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "thiserror" -version = "1.0.56" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -633,51 +882,80 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinystr" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] [[package]] -name = "tinyvec_macros" -version = "0.1.1" +name = "toml" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ "indexmap", "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ "winnow", ] +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -686,9 +964,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -697,100 +975,104 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", ] [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "once_cell", - "regex", + "regex-automata", "sharded-slab", "thread_local", "tracing", "tracing-core", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] -name = "unicode-normalization" -version = "0.1.22" +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "url" -version = "2.5.0" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] -name = "wasm-bindgen" -version = "0.2.90" +name = "wasip2" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "cfg-if", - "wasm-bindgen-macro", + "wit-bindgen 0.57.1", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.90" +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "bumpalo", - "log", + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +dependencies = [ + "cfg-if", "once_cell", - "proc-macro2", - "quote", - "syn", + "rustversion", + "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -798,28 +1080,65 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" dependencies = [ "js-sys", "wasm-bindgen", @@ -827,189 +1146,242 @@ dependencies = [ [[package]] name = "webbrowser" -version = "0.8.12" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b2391658b02c27719fc5a0a73d6e696285138e8b12fba9d4baa70451023c71" +checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" dependencies = [ "core-foundation", - "home", "jni", "log", "ndk-context", - "objc", - "raw-window-handle", + "objc2", + "objc2-foundation", "url", "web-sys", ] -[[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-util" -version = "0.1.6" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "winapi", + "windows-sys", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-targets 0.42.2", + "windows-link", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "winnow" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ - "windows-targets 0.52.0", + "memchr", ] [[package]] -name = "windows-targets" -version = "0.42.2" +name = "wit-bindgen" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "wit-bindgen-rust-macro", ] [[package]] -name = "windows-targets" -version = "0.52.0" +name = "wit-bindgen" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", -] +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" +name = "wit-bindgen-core" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" +name = "wit-bindgen-rust" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] [[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" +name = "wit-bindgen-rust-macro" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] [[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" +name = "wit-component" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] [[package]] -name = "windows_i686_gnu" -version = "0.42.2" +name = "wit-parser" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] -name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +name = "workspace-gen" +version = "0.1.0" +dependencies = [ + "anyhow", + "bpaf", + "serde", + "toml", +] [[package]] -name = "windows_i686_msvc" -version = "0.42.2" +name = "writeable" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] -name = "windows_i686_msvc" -version = "0.52.0" +name = "yoke" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] [[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" +name = "yoke-derive" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] [[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" +name = "zerofrom" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] [[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" +name = "zerofrom-derive" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] [[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" +name = "zerotrie" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" +name = "zerovec" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" +name = "zerovec-derive" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "winnow" -version = "0.5.37" +name = "zmij" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5" -dependencies = [ - "memchr", -] +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 15f7fdf..7123c7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cargo-hackerman" -version = "0.2.9" -edition = "2021" +version = "0.2.10" +edition = "2024" description = "Workspace hack management and package/feature query" license = "MIT OR Apache-2.0" repository = "https://github.com/pacak/hackerman/" @@ -12,23 +12,25 @@ exclude = ["TODO", "test_workspaces"] [dependencies] anyhow = "1.0.52" bpaf = { version = "0.9.9", features = ["derive", "autocomplete", "docgen"] } -cargo-platform = "0.1" -cargo_metadata = { version = "0.18" } +cargo_metadata = { version = "0.23" } dot = "0.1.4" pathdiff = { version = "0.2", features = ["camino"] } -petgraph = "0.6.0" +petgraph = "0.8.3" semver = "1.0" -serde = "=1.0.196" +serde = "1" serde_json = "1.0" target-spec = "3.0" tempfile = { version = "3.3.0" } -toml_edit = "0.21" +toml_edit = "0.25" tracing = "0.1.29" tracing-subscriber = { version = "0.3.5", default-features = false, features = [ "alloc", "env-filter", "registry", "std", "fmt" ] } -webbrowser = { version = "0.8.10", optional = true } +webbrowser = { version = "1.2.1", optional = true } [features] bright-color = ["bpaf/bright-color"] default = ["dull-color"] dull-color = ["bpaf/dull-color"] + +[workspace] +members = ["./workspace-gen"] diff --git a/README.md b/README.md index 4a81870..067d6de 100644 --- a/README.md +++ b/README.md @@ -217,7 +217,7 @@ You can pass **`--help`** twice for more detailed help Unify crate dependencies across individual crates in the workspace -**Usage**: **`cargo hackerman`** **`hack`** _`CARGO_OPTS`_ \[**`--dry`**\] \[**`--lock`**\] \[**`-D`**\] +**Usage**: **`cargo hackerman`** **`hack`** _`CARGO_OPTS`_ \[**`--dry`**\] \[**`--lock`**\] You can undo those changes using `cargo hackerman restore`. @@ -251,8 +251,6 @@ You can undo those changes using `cargo hackerman restore`. lock = true ``` -- **`-D`**, **`--no-dev`** — - Don't unify dev dependencies - **`-h`**, **`--help`** — Prints help information @@ -300,7 +298,7 @@ Check if unification is required and if checksums are correct Similar to `cargo-hackerman hack --dry`, but also sets exit status to 1 so you can use it as part of CI process -**Usage**: **`cargo hackerman`** **`check`** _`CARGO_OPTS`_ \[**`-D`**\] +**Usage**: **`cargo hackerman`** **`check`** _`CARGO_OPTS`_ **Cargo options:** - **` --manifest-path`**=_`PATH`_ — @@ -317,8 +315,6 @@ Similar to `cargo-hackerman hack --dry`, but also sets exit status to 1 so you c **Available options:** -- **`-D`**, **`--no-dev`** — - Don't unify dev dependencies - **`-h`**, **`--help`** — Prints help information diff --git a/src/explain.rs b/src/explain.rs index ee2309f..571ffb2 100644 --- a/src/explain.rs +++ b/src/explain.rs @@ -1,5 +1,5 @@ use crate::{ - feat_graph::{FeatGraph, HasIndex}, + feat_graph::{CrateInstance, FeatGraph, HasIndex}, metadata::{DepKindInfo, Link}, }; @@ -11,27 +11,28 @@ use semver::Version; use std::collections::BTreeSet; use tracing::{debug, info}; +/// Find all the packages that can be a starting point for a query to display (display purposes) +/// +/// Match all crates by name, with refining by a feature name and/or version number fn collect_packages( fg: &mut FeatGraph, - krate: &str, - feature: Option<&String>, + feature: Option<&str>, version: Option<&Version>, ) -> Vec { fg.features .node_indices() .filter(|&ix| { - if let Some(fid) = fg.features[ix].fid() { - let package = fid.pid.package(); - // name must match. - // feature must match if given, otherwise look for base - // version must match if given - package.name == krate - && feature.map_or(fid.pid.base() == fid, |f| fid.pid.named(f) == fid) - && version.map_or(true, |v| package.version == *v) - } else { - false - } + let Some(fid) = fg.features[ix].fid() else { + return false; + }; + let package = fid.pid.package(); + // name must match. + // feature must match if given, otherwise look for base + // version must match if given + package.name == krate + && version.is_none_or(|v| package.version == *v) + && (feature.is_none() || feature == fid.name()) }) .collect::>() } @@ -39,7 +40,7 @@ fn collect_packages( pub fn tree<'a>( fg: &'a mut FeatGraph<'a>, krate: Option<&String>, - feature: Option<&String>, + feature: Option<&str>, version: Option<&Version>, package_nodes: bool, workspace: bool, @@ -54,7 +55,7 @@ pub fn tree<'a>( let members = fg.workspace_members.clone(); members .iter() - .map(|f| fg.fid_index(f.base())) + .map(|f| fg.fid_index(f.base(CrateInstance::Target))) .collect::>() } }; @@ -82,6 +83,7 @@ pub fn tree<'a>( node }; nodes.insert(this_node); + for edge in g.edges_directed(node, petgraph::EdgeDirection::Outgoing) { if package_nodes { new_edges.insert(( @@ -97,7 +99,7 @@ pub fn tree<'a>( if package_nodes { for (a, b) in new_edges { - let a = a.get_index(fg)?; + let a = a.get_index(fg, CrateInstance::Target)?; if a != b { let link = Link { optional: false, @@ -119,7 +121,7 @@ pub fn tree<'a>( pub fn explain<'a>( fg: &'a mut FeatGraph<'a>, krate: &str, - feature: Option<&String>, + feature: Option<&str>, version: Option<&Version>, package_nodes: bool, stdout: bool, @@ -178,7 +180,7 @@ pub fn explain<'a>( if package_nodes { for (a, b) in new_edges { - let a = a.get_index(fg)?; + let a = a.get_index(fg, CrateInstance::Target)?; if a != b { let link = Link { optional: false, diff --git a/src/feat_graph.rs b/src/feat_graph.rs index 575744c..7109952 100644 --- a/src/feat_graph.rs +++ b/src/feat_graph.rs @@ -1,16 +1,23 @@ use crate::hack::Collect; use crate::metadata::{DepKindInfo, Link}; -use cargo_metadata::{Metadata, Package, PackageId, Source}; -use cargo_platform::Cfg; +use cargo_metadata::{Metadata, Package, PackageId, Source, cargo_platform::Cfg}; use dot::{GraphWalk, Labeller}; +use petgraph::Graph; use petgraph::graph::{EdgeIndex, NodeIndex}; use petgraph::visit::{Dfs, EdgeFiltered, EdgeRef}; -use petgraph::Graph; use std::borrow::Cow; use std::collections::{BTreeMap, BTreeSet}; use std::ops::Index; use tracing::{debug, error, info, trace}; +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum CrateInstance { + /// Crate is compiled for the host (build scripts, proc macros) + Host, + /// Crate is compiled for the target + Target, +} + #[derive(Copy, Clone, Ord, PartialEq, Eq, PartialOrd, Debug)] /// An node for feature graph pub enum Feature<'a> { @@ -47,8 +54,8 @@ impl<'a> Feature<'a> { #[must_use] pub fn package_id(&self) -> Option<&PackageId> { - let Pid(pid, meta) = self.pid()?; - Some(&meta.packages[pid].id) + let Pid { key, metadata } = self.pid()?; + Some(&metadata.packages[key].id) } #[must_use] @@ -76,7 +83,7 @@ pub struct FeatGraph<'a> { pub fid_cache: BTreeMap, NodeIndex>, /// cargo metadata - meta: &'a Metadata, + metadata: &'a Metadata, pub platforms: Vec<&'a str>, pub cfgs: Vec, @@ -91,7 +98,7 @@ impl<'a> Index> for FeatGraph<'a> { type Output = NodeIndex; fn index(&self, index: Pid<'a>) -> &Self::Output { - &self.fid_cache[&index.root()] + &self.fid_cache[&index.root(CrateInstance::Target)] } } @@ -167,26 +174,26 @@ impl<'a> FeatGraph<'a> { } pub fn init( - meta: &'a Metadata, + metadata: &'a Metadata, platforms: Vec<&'a str>, cfgs: Vec, ) -> anyhow::Result { - if meta.resolve.is_none() { + if metadata.resolve.is_none() { anyhow::bail!("Cargo couldn't produce resolved dependencies") } - let cache = meta + let cache = metadata .packages .iter() .enumerate() - .map(|(ix, package)| (&package.id, Pid(ix, meta))) + .map(|(key, package)| (&package.id, Pid { key, metadata })) .collect::>(); let mut features = Graph::new(); let root = features.add_node(Feature::Root); let mut graph = Self { - workspace_members: meta + workspace_members: metadata .workspace_members .iter() .filter_map(|pid| cache.get(pid)) @@ -199,15 +206,34 @@ impl<'a> FeatGraph<'a> { triggers: Vec::new(), fid_cache: BTreeMap::new(), cache, - meta, + metadata, cfgs, focus_nodes: None, focus_edges: None, focus_targets: None, }; - for (ix, package) in meta.packages.iter().enumerate() { - graph.add_package(ix, package, &meta.packages)?; + for (ix, package) in metadata.packages.iter().enumerate() { + let instance = if is_proc_macro(package) { + CrateInstance::Host + } else { + CrateInstance::Target + }; + graph.add_package(ix, package, &metadata.packages, instance)?; + // Process the opposite instance so that dependency edges exist + // for both Host and Target. This matters when a non-proc-macro + // package (e.g. cxx-build) is reached as Host through a build- + // dependency edge but its dependency edges were only wired for + // Target. + graph.add_package( + ix, + package, + &metadata.packages, + match instance { + CrateInstance::Host => CrateInstance::Target, + CrateInstance::Target => CrateInstance::Host, + }, + )?; } graph.rebuild_cache()?; @@ -294,19 +320,51 @@ impl<'a> FeatGraph<'a> { fn add_package( &mut self, - ix: usize, + key: usize, package: &'a Package, packages: &'a [Package], + instance: CrateInstance, ) -> anyhow::Result<()> { - debug!("== adding package {}", package.id); - let this = Pid(ix, self.meta); - let base_ix = self.fid_index(this.base()); + // Proc macro crates are always compiled for the host, regardless of how they're reached + let instance = if package + .targets + .iter() + .any(|t| t.kind.contains(&cargo_metadata::TargetKind::ProcMacro)) + { + CrateInstance::Host + } else { + instance + }; + + debug!("== adding package {} ({:?})", package.id, instance); + let this = Pid { + key, + metadata: self.metadata, + }; + let base_ix = self.fid_index(this.base(instance)); + self.fid_index(this.base(match instance { + CrateInstance::Host => CrateInstance::Target, + CrateInstance::Target => CrateInstance::Host, + })); let workspace_member = self.workspace_members.contains(&this); + let primary_instance = if is_proc_macro(package) { + CrateInstance::Host + } else { + CrateInstance::Target + }; // root contains links to all the workspace members - if workspace_member { - self.add_edge(self.root, this, false, DepKindInfo::NORMAL)?; + // Only add for the primary instance — the second call from init + // skips this to avoid giving proc-macro members a bogus root link. + if workspace_member && instance == CrateInstance::Target && instance == primary_instance { + self.add_edge( + self.root, + this, + false, + DepKindInfo::NORMAL, + CrateInstance::Target, + )?; } // resolve and cache crate dependencies and create a cache mapping name to dep @@ -317,24 +375,37 @@ impl<'a> FeatGraph<'a> { continue; } - let source_matches = |a: Option<&Source>, b: Option<&String>| match (a, b) { + let source_matches = |a: Option<&Source>, b: Option<&Source>| match (a, b) { (None, None) => true, (Some(a), Some(b)) => { - if &a.repr == b || (a.repr.starts_with("git") && a.repr.starts_with(b)) { + if a == b || (a.repr.starts_with("git") && a.repr.starts_with(&b.repr)) { true } else { - trace!("ignoring a candidate {package:?} for {dep:?} due to source mismatch: {a:?} != {b:?}"); + trace!( + "ignoring a candidate {package:?} for {dep:?} due to source mismatch: {a:?} != {b:?}" + ); false } } _ => false, }; // get resolved package - should be there in at most one matching copy... - let resolved = match packages.iter().find(|p| { - p.name == dep.name - && dep.req.matches(&p.version) - && source_matches(p.source.as_ref(), dep.source.as_ref()) - }) { + // Prefer a candidate whose source matches the dep's declared source; + // fall back to any candidate matching by name+version so that + // `[patch]` redirects are still handled. + let resolved = packages + .iter() + .find(|p| { + p.name == dep.name + && dep.req.matches(&p.version) + && source_matches(p.source.as_ref(), dep.source.as_ref()) + }) + .or_else(|| { + packages + .iter() + .find(|p| p.name == dep.name && dep.req.matches(&p.version)) + }); + let resolved = match resolved { Some(res) => res, None => { debug!( @@ -346,15 +417,33 @@ impl<'a> FeatGraph<'a> { } }; + // Determine the instance of the dependency: + // - Host instance always produces Host dependencies + // - Target + build link -> Host + // - Target + normal link -> Target + // - Proc-macro dependencies are always compiled for the host + let dep_instance = match instance { + CrateInstance::Host => CrateInstance::Host, + CrateInstance::Target => { + if dep.kind == cargo_metadata::DependencyKind::Build || is_proc_macro(resolved) + { + CrateInstance::Host + } else { + CrateInstance::Target + } + } + }; + // feature dependencies: // - // - optional dependencies are linked from named feature - // - requred dependenceis are linked fromb base + // - optional dependencies are linked from a node marked as + // `Feat::Dep()` so we can distinguish it from + // real named features of the parent crate + // - required dependencies are linked from base let this = if dep.optional { - match dep.rename.as_ref() { - Some(name) => this.named(name).get_index(self)?, - None => this.named(&dep.name).get_index(self)?, - } + let name = dep.rename.as_deref().unwrap_or(dep.name.as_ref()); + this.dep_node(name, dep_instance) + .get_index(self, dep_instance)? } else { base_ix }; @@ -362,60 +451,132 @@ impl<'a> FeatGraph<'a> { // dependencies that have default target are linked to that target // otherwise dependencies are linked to let remote = if dep.uses_default_features { - Some(self.add_edge(this, resolved, false, dep.into())?) + Some(self.add_edge(this, resolved, false, dep.into(), dep_instance)?) } else if let Some(pid) = self.cache.get(&resolved.id) { - let fid = pid.base(); - Some(self.add_edge(this, fid, false, dep.into())?) + let fid = pid.base(dep_instance); + Some(self.add_edge(this, fid, false, dep.into(), dep_instance)?) } else { None }; // if additional features on dependency are required - we add them all for feat in &dep.features { - self.add_edge(this, (resolved, feat.as_str()), false, dep.into())?; + self.add_edge( + this, + (resolved, feat.as_str()), + false, + dep.into(), + dep_instance, + )?; } // for remote dependencies we store the resolved ifo in order to deal with renames if let Some(remote) = remote { - let name = dep.rename.clone().unwrap_or_else(|| resolved.name.clone()); - deps.insert(name, (resolved, dep, remote)); + let name = dep + .rename + .as_ref() + .map_or(resolved.name.as_str().to_string(), |n| n.clone()); + + // For optional dependencies, `this` is the named feature + // representing the dep (e.g. `crate/`). It is the + // node that `dep:` syntax in features should + // enable, so we remember it for later use in the feature + // loop. + let named = if dep.optional { Some(this) } else { None }; + deps.insert(name, (resolved, dep, remote, dep_instance, named)); } } for (this_feat, feat_deps) in &package.features { - let feat_ix = self.fid_index(this.named(this_feat)); - self.add_edge(feat_ix, base_ix, false, DepKindInfo::NORMAL)?; + let feat_ix = self.fid_index(this.named(this_feat, instance)); + self.add_edge(feat_ix, base_ix, false, DepKindInfo::NORMAL, instance)?; for feat_dep in feat_deps.iter() { match FeatTarget::from(feat_dep.as_str()) { FeatTarget::Named { name } => { - self.add_edge(feat_ix, this.named(name), false, DepKindInfo::NORMAL)?; + self.add_edge( + feat_ix, + this.named(name, instance), + false, + DepKindInfo::NORMAL, + instance, + )?; } FeatTarget::Dependency { krate } => { - if let Some(&(_dep, link, remote)) = deps.get(krate) { - self.add_edge(feat_ix, remote, true, link.into())?; + if let Some(&(_pkg, _link, _remote, _dep_instance, named)) = deps.get(krate) + { + // `dep:` enables the optional dep. The + // activation has to go through the named + // feature (e.g. `crate/`) so that + // the explicit features the dep was declared + // with (`dep.features`) are also picked up + // when the resulting graph is walked. + let Some(named) = named else { + debug!("dep:<{krate}> only applies to optional dependencies"); + continue; + }; + self.add_edge(feat_ix, named, true, DepKindInfo::NORMAL, instance)?; } else { debug!("skipping disabled optional dependency {krate}"); } } FeatTarget::Remote { krate, feat } => { - if let Some(&(dep, link, _remote)) = deps.get(krate) { - self.add_edge(feat_ix, (dep, feat), true, link.into())?; + if let Some(&(pkg, link, _remote, dep_instance, _named)) = deps.get(krate) { + if link.optional { + let weak_dep = this.dep_node(krate, dep_instance); + self.add_edge( + feat_ix, + weak_dep, + true, + DepKindInfo::NORMAL, + instance, + )?; + if let Some(pid) = self.cache.get(&pkg.id).copied() { + let weak_feat = pid.named(feat, dep_instance); + self.fid_index(weak_feat); + let trigger = Trigger { + package: this, + feature: this.named(this_feat, instance), + weak_dep, + weak_feat, + }; + self.triggers.push(trigger); + } + } else if let Some(pid) = self.cache.get(&pkg.id).copied() { + self.add_edge( + feat_ix, + pid.named(feat, dep_instance), + true, + link.into(), + dep_instance, + )?; + } } else { debug!("skipping disabled optional dependency {krate}"); } } FeatTarget::Cond { krate, feat } => { - if let Some(dep) = deps - .get(krate) - .and_then(|&(dep, _link, _remote)| self.cache.get(&dep.id).copied()) - { - let trigger = Trigger { - package: this, - feature: this.named(this_feat), - weak_dep: this.named(krate), - weak_feat: dep.named(feat), - }; - self.triggers.push(trigger); + if let Some(&(pkg, link, _remote, dep_instance, _named)) = deps.get(krate) { + if link.optional { + if let Some(pid) = self.cache.get(&pkg.id).copied() { + let weak_feat = pid.named(feat, dep_instance); + self.fid_index(weak_feat); + let trigger = Trigger { + package: this, + feature: this.named(this_feat, instance), + weak_dep: this.dep_node(krate, dep_instance), + weak_feat, + }; + self.triggers.push(trigger); + } + } else if let Some(pid) = self.cache.get(&pkg.id).copied() { + self.add_edge( + feat_ix, + pid.named(feat, dep_instance), + true, + link.into(), + dep_instance, + )?; + } } else { debug!("skipping disabled optional dependency {krate}"); } @@ -433,13 +594,14 @@ impl<'a> FeatGraph<'a> { b: B, optional: bool, kind: DepKindInfo, + _source_instance: CrateInstance, ) -> anyhow::Result where A: HasIndex<'a>, B: HasIndex<'a>, { - let a = a.get_index(self)?; - let b = b.get_index(self)?; + let a = a.get_index(self, _source_instance)?; + let b = b.get_index(self, _source_instance)?; trace!( "adding {}edge {a:?} -> {b:?}: {kind:?}\n\t{:?}\n\t{:?}", if optional { "optional " } else { "" }, @@ -465,44 +627,69 @@ impl<'a> FeatGraph<'a> { } #[derive(Copy, Clone)] -pub struct Pid<'a>(usize, &'a Metadata); +pub struct Pid<'a> { + /// key for this package in cargo metadata index + key: usize, + metadata: &'a Metadata, +} + +fn is_proc_macro(package: &Package) -> bool { + package + .targets + .iter() + .any(|t| t.kind.contains(&cargo_metadata::TargetKind::ProcMacro)) +} impl<'a> Pid<'a> { #[must_use] pub fn package(self) -> &'a cargo_metadata::Package { - &self.1.packages[self.0] + &self.metadata.packages[self.key] + } + + pub fn is_proc_macro(self) -> bool { + is_proc_macro(self.package()) } } impl<'a> Pid<'a> { #[must_use] - pub fn root(self) -> Fid<'a> { + pub fn root(self, instance: CrateInstance) -> Fid<'a> { if self.package().features.contains_key("default") { - self.named("default") + self.named("default", instance) } else { - self.base() + self.base(instance) } } #[must_use] - pub const fn base(self) -> Fid<'a> { + pub const fn base(self, instance: CrateInstance) -> Fid<'a> { Fid { pid: self, dep: Feat::Base, + instance, } } #[must_use] - pub const fn named(self, name: &'a str) -> Fid<'a> { + pub const fn named(self, name: &'a str, instance: CrateInstance) -> Fid<'a> { Fid { pid: self, dep: Feat::Named(name), + instance, + } + } + #[must_use] + pub const fn dep_node(self, name: &'a str, instance: CrateInstance) -> Fid<'a> { + Fid { + pid: self, + dep: Feat::Dep(name), + instance, } } } impl<'a> PartialEq for Pid<'a> { fn eq(&self, other: &Self) -> bool { - self.0 == other.0 + self.key == other.key } } @@ -516,14 +703,14 @@ impl<'a> PartialOrd for Pid<'a> { impl<'a> Ord for Pid<'a> { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.0.cmp(&other.0) + self.key.cmp(&other.key) } } impl std::fmt::Debug for Pid<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let meta = &self.1.packages[self.0]; - write!(f, "Pid({} / {})", self.0, meta.id) + let meta = &self.metadata.packages[self.key]; + write!(f, "Pid({} / {})", self.key, meta.id) } } @@ -532,6 +719,17 @@ pub struct Fid<'a> { /// this feature originates from pub pid: Pid<'a>, pub dep: Feat<'a>, + /// whether this crate instance is compiled for host or target + pub instance: CrateInstance, +} + +impl<'a> Fid<'a> { + pub(crate) fn name(self) -> Option<&'a str> { + match self.dep { + Feat::Named(n) => Some(n), + Feat::Base | Feat::Dep(_) => None, + } + } } impl std::fmt::Display for Fid<'_> { @@ -540,6 +738,11 @@ impl std::fmt::Display for Fid<'_> { match self.dep { Feat::Base => write!(f, "{id}"), Feat::Named(name) => write!(f, "{id}:{name}"), + Feat::Dep(name) => write!(f, "{id}:dep:{name}"), + }?; + match self.instance { + CrateInstance::Host => write!(f, " (host)"), + CrateInstance::Target => Ok(()), } } } @@ -549,6 +752,7 @@ impl std::fmt::Display for Feat<'_> { match self { Feat::Base => f.write_str(":base:"), Feat::Named(name) => f.write_str(name), + Feat::Dep(name) => write!(f, "dep:{name}"), } } } @@ -559,6 +763,12 @@ pub enum Feat<'a> { Base, /// internally defined named feature Named(&'a str), + /// node representing an optional dependency. The `&'a str` is the + /// dependency's name (or rename) and matches the name used to refer + /// to it in `dep:` feature syntax. It must NOT be reported as + /// a feature of the parent crate, since enabling it means + /// enabling the dependency, not a feature of the parent. + Dep(&'a str), } impl<'a> GraphWalk<'a, NodeIndex, EdgeIndex> for FeatGraph<'a> { @@ -598,7 +808,7 @@ impl<'a> Labeller<'a, NodeIndex, EdgeIndex> for FeatGraph<'a> { let fid = self.features[*node].fid()?; match fid.dep { Feat::Base => Some(dot::LabelText::label("octagon")), - Feat::Named(_) => None, + Feat::Named(_) | Feat::Dep(_) => None, } } @@ -622,6 +832,11 @@ impl<'a> Labeller<'a, NodeIndex, EdgeIndex> for FeatGraph<'a> { fmt.push('\n'); fmt.push_str(name); } + Feat::Dep(name) => { + fmt.push('\n'); + fmt.push_str("dep:"); + fmt.push_str(name); + } } dot::LabelText::LabelStr(fmt.into()) @@ -648,10 +863,20 @@ impl<'a> Labeller<'a, NodeIndex, EdgeIndex> for FeatGraph<'a> { } fn node_color(&'a self, node: &NodeIndex) -> Option> { - self.focus_targets - .as_ref()? - .contains(node) - .then(|| dot::LabelText::LabelStr("pink".into())) + if self + .focus_targets + .as_ref() + .is_some_and(|t| t.contains(node)) + { + Some(dot::LabelText::LabelStr("pink".into())) + } else if self.features[*node] + .fid() + .is_some_and(|fid| fid.instance == CrateInstance::Host) + { + Some(dot::LabelText::LabelStr("lightblue".into())) + } else { + None + } } fn edge_end_arrow(&'a self, _e: &EdgeIndex) -> dot::Arrow { @@ -671,7 +896,13 @@ impl<'a> Labeller<'a, NodeIndex, EdgeIndex> for FeatGraph<'a> { } fn edge_color(&'a self, e: &EdgeIndex) -> Option> { - if self.features[*e].optional { + let source = self.features.edge_endpoints(*e).unwrap().0; + if self.features[source] + .fid() + .is_some_and(|fid| fid.instance == CrateInstance::Host) + { + Some(dot::LabelText::label("lightblue")) + } else if self.features[*e].optional { Some(dot::LabelText::label("grey")) } else { Some(dot::LabelText::label("black")) @@ -684,50 +915,74 @@ impl<'a> Labeller<'a, NodeIndex, EdgeIndex> for FeatGraph<'a> { } pub trait HasIndex<'a> { - fn get_index(self, graph: &mut FeatGraph<'a>) -> anyhow::Result; + fn get_index( + self, + graph: &mut FeatGraph<'a>, + instance: CrateInstance, + ) -> anyhow::Result; } impl HasIndex<'_> for NodeIndex { - fn get_index(self, _graph: &mut FeatGraph) -> anyhow::Result { + fn get_index( + self, + _graph: &mut FeatGraph, + _instance: CrateInstance, + ) -> anyhow::Result { Ok(self) } } impl<'a> HasIndex<'a> for Fid<'a> { - fn get_index(self, graph: &mut FeatGraph<'a>) -> anyhow::Result { + fn get_index( + self, + graph: &mut FeatGraph<'a>, + _instance: CrateInstance, + ) -> anyhow::Result { Ok(graph.fid_index(self)) } } impl<'a> HasIndex<'a> for Pid<'a> { - fn get_index(self, graph: &mut FeatGraph<'a>) -> anyhow::Result { + fn get_index( + self, + graph: &mut FeatGraph<'a>, + instance: CrateInstance, + ) -> anyhow::Result { if self.package().features.contains_key("default") { - Ok(graph.fid_index(self.named("default"))) + Ok(graph.fid_index(self.named("default", instance))) } else { - Ok(graph.fid_index(self.base())) + Ok(graph.fid_index(self.base(instance))) } } } impl<'a> HasIndex<'a> for &'a Package { - fn get_index(self, graph: &mut FeatGraph<'a>) -> anyhow::Result { + fn get_index( + self, + graph: &mut FeatGraph<'a>, + instance: CrateInstance, + ) -> anyhow::Result { (*graph .cache .get(&self.id) .ok_or_else(|| anyhow::anyhow!("No cached value for {:?}", self.id))?) - .get_index(graph) + .get_index(graph, instance) } } impl<'a> HasIndex<'a> for (&'a Package, &'a str) { - fn get_index(self, graph: &mut FeatGraph<'a>) -> anyhow::Result { + fn get_index( + self, + graph: &mut FeatGraph<'a>, + instance: CrateInstance, + ) -> anyhow::Result { let package_id = &self.0.id; let feat = self.1; let pid = *graph .cache .get(package_id) .ok_or_else(|| anyhow::anyhow!("No cached value for {package_id:?}"))?; - pid.named(feat).get_index(graph) + pid.named(feat, instance).get_index(graph, instance) } } @@ -755,7 +1010,7 @@ impl<'a> From<&'a str> for FeatTarget<'a> { impl Fid<'_> { #[must_use] - /// Create a base feature from possibly named one + /// Create a base feature from possibly named one, preserving instance pub const fn get_base(&self) -> Self { Self { dep: Feat::Base, diff --git a/src/hack.rs b/src/hack.rs index bf4e9ac..a0415cb 100644 --- a/src/hack.rs +++ b/src/hack.rs @@ -1,13 +1,12 @@ #![allow(clippy::similar_names)] use crate::{ - feat_graph::{Feat, FeatGraph, Pid}, + feat_graph::{CrateInstance, Feat, FeatGraph, Pid}, metadata::DepKindInfo, source::ChangePackage, toml::set_dependencies, }; -use cargo_metadata::Metadata; -use cargo_platform::Cfg; +use cargo_metadata::{Metadata, cargo_platform::Cfg}; use petgraph::{ graph::NodeIndex, visit::{Dfs, DfsPostOrder, EdgeFiltered, EdgeRef, NodeFiltered, VisitMap, Walker}, @@ -23,16 +22,14 @@ fn force_config(var: &mut bool, name: &str, meta: &serde_json::Value) -> Option< pub fn hack( dry: bool, mut lock: bool, - mut no_dev: bool, meta: &Metadata, triplets: Vec<&str>, cfgs: Vec, ) -> anyhow::Result { force_config(&mut lock, "lock", &meta.workspace_metadata); - force_config(&mut no_dev, "no-dev", &meta.workspace_metadata); let mut fg = FeatGraph::init(meta, triplets, cfgs)?; - let changeset = get_changeset(&mut fg, no_dev)?; + let changeset = get_changeset(&mut fg)?; let has_changes = !changeset.is_empty(); if dry { @@ -55,7 +52,7 @@ pub fn hack( println!("{path}"); for change in changeset { let t = match change.ty { - Ty::Dev => "dev ", + Ty::Build => "build ", Ty::Norm => "", }; println!( @@ -124,7 +121,7 @@ pub enum Collect<'a> { /// current target only, normal and build dependencies globally, dev dependencies for workspace DevTarget, NoDev, - MemberDev(Pid<'a>), + MemberBuild(Pid<'a>), } // we are doing 4 types of passes: @@ -148,7 +145,7 @@ fn collect_features_from( // last_edge.set(Some(e)); match filter { Collect::AllTargets => true, - Collect::Target | Collect::NoDev | Collect::DevTarget | Collect::MemberDev(_) => e + Collect::Target | Collect::NoDev | Collect::DevTarget | Collect::MemberBuild(_) => e .weight() .satisfies(fg.features[e.source()], filter, &fg.platforms, &fg.cfgs), Collect::NormalOnly => e.weight().is_normal(), @@ -157,22 +154,24 @@ fn collect_features_from( loop { while let Some(ix) = dfs.next(&g) { - if let Some(fid) = fg.features[ix].fid() { - if let Some(parent) = fg.fid_cache.get(&fid.get_base()) { - to.entry(*parent).or_default().insert(ix); - } + if let Some(fid) = fg.features[ix].fid() + && let Some(parent) = fg.fid_cache.get(&fid.get_base()) + { + to.entry(*parent).or_default().insert(ix); } } for t in fg.triggers.iter() { - let package = fg.fid_cache[&t.package.base().get_base()]; - let feature = fg.fid_cache[&t.feature]; // .unwrap(); + let package = fg.fid_cache[&t.package.base(t.feature.instance).get_base()]; + let feature = fg.fid_cache[&t.feature]; let weak_dep = fg.fid_cache[&t.weak_dep]; let weak_feat = fg.fid_cache[&t.weak_feat]; - if let Some(dep) = to.get(&package) { - if dep.contains(&feature) && dep.contains(&weak_dep) && added.insert(weak_feat) { - to_visit.push(weak_feat); - } + if let Some(dep) = to.get(&package) + && dep.contains(&feature) + && dep.contains(&weak_dep) + && added.insert(weak_feat) + { + to_visit.push(weak_feat); } } @@ -186,7 +185,7 @@ fn collect_features_from( #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] pub enum Ty { - Dev, + Build, Norm, } @@ -194,7 +193,7 @@ impl Ty { #[must_use] pub const fn table_name(&self) -> &'static str { match self { - Ty::Dev => "dev-dependencies", + Ty::Build => "build-dependencies", Ty::Norm => "dependencies", } } @@ -203,15 +202,25 @@ impl Ty { impl std::fmt::Display for Ty { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Ty::Dev => f.write_str("dev"), + Ty::Build => f.write_str("build"), Ty::Norm => f.write_str("norm"), } } } -pub fn get_changeset<'a>(fg: &mut FeatGraph<'a>, no_dev: bool) -> anyhow::Result> { +pub fn get_changeset<'a>(fg: &mut FeatGraph<'a>) -> anyhow::Result> { info!("==== Calculating changeset for hack"); + // minimal feature unification: + // + // 1. collect superset of all the features enabled on a workspace as a whole, separately normal + // and build (proc macro) dependencies + // + // 2. for every workspace member, starting from leaves look for all their dependencies. For + // every dependency crate, look for features not present on that crate but present in the + // workspace. Add this dependency directly to current workspace member with missing + // workspace features enabled + // dump(fg)?; let mut changed = BTreeMap::new(); // loop { @@ -226,7 +235,7 @@ pub fn get_changeset<'a>(fg: &mut FeatGraph<'a>, no_dev: bool) -> anyhow::Result &mut Dfs::new(&fg.features, fg.root), fg, &mut raw_workspace_feats, - Collect::NormalOnly, + Collect::NoDev, ); // For reasons unknown cargo resolves dependencies for all the targets including those @@ -242,6 +251,16 @@ pub fn get_changeset<'a>(fg: &mut FeatGraph<'a>, no_dev: bool) -> anyhow::Result ); raw_workspace_feats.retain(|k, _| filtered_workspace_feats.contains_key(k)); + let filtered_workspace_host_feats: DetachedDepTree = filtered_workspace_feats + .iter() + .filter(|(key, _)| { + fg.features[**key] + .fid() + .is_some_and(|fid| fid.instance == CrateInstance::Host) + }) + .map(|(&k, v)| (k, v.clone())) + .collect(); + info!( "Accumulated workspace dependencies{}", show_detached_dep_tree(&raw_workspace_feats, fg) @@ -266,10 +285,15 @@ pub fn get_changeset<'a>(fg: &mut FeatGraph<'a>, no_dev: bool) -> anyhow::Result seen.insert(pid); let package = pid.package(); + let instance = if pid.is_proc_macro() { + CrateInstance::Host + } else { + CrateInstance::Target + }; let fid = if package.features.contains_key("default") { - pid.named("default") + pid.named("default", instance) } else { - pid.base() + pid.base(instance) }; if let Some(&ix) = fg.fid_cache.get(&fid) { res.push((pid, ix)); @@ -299,79 +323,96 @@ pub fn get_changeset<'a>(fg: &mut FeatGraph<'a>, no_dev: bool) -> anyhow::Result ); for (&dep, feats) in &deps_feats { - if let Some(ws_feats) = raw_workspace_feats.get(&dep) { - if ws_feats != feats { - if let Some(&missing_feat) = ws_feats.difference(feats).next() { - info!("\t{member:?} lacks {}", fg.features[missing_feat]); - - changed - .entry(member) - .or_insert_with(BTreeMap::default) - .insert((Ty::Norm, dep), ws_feats.clone()); - - let new_dep = - fg.add_edge(member_ix, missing_feat, false, DepKindInfo::NORMAL)?; - dfs.move_to(new_dep); - - trace!("Performing one more iteration on {member:?}"); - continue 'dependency; - } - } + if let Some(ws_feats) = raw_workspace_feats.get(&dep) + && ws_feats != feats + && let Some(&missing_feat) = ws_feats.difference(feats).next() + { + info!("\t{member:?} lacks {}", fg.features[missing_feat]); + + let ty = if let Some(fid) = fg.features[missing_feat].fid() + && (fid.instance == CrateInstance::Host || fid.pid.is_proc_macro()) + { + Ty::Build + } else { + Ty::Norm + }; + + changed + .entry(member) + .or_insert_with(BTreeMap::default) + .insert((ty, dep), ws_feats.clone()); + + let new_dep = fg.add_edge( + member_ix, + missing_feat, + false, + DepKindInfo::NORMAL, + CrateInstance::Target, + )?; + dfs.move_to(new_dep); + + trace!("Performing one more iteration on {member:?}"); + continue 'dependency; } } break; } - if no_dev { - continue; - } - // at this point dep_feats contains all the normal features used by {member}. // we'll use it to filter dep dependencies if any. if !member .package() .dependencies .iter() - .any(|d| d.kind == cargo_metadata::DependencyKind::Development) + .any(|d| d.kind == cargo_metadata::DependencyKind::Build) { - debug!("No dev dependencies for {member:?}, skipping"); + debug!("No build dependencies for {member:?}, skipping"); continue; } let mut dfs = Dfs::new(&fg.features, member_ix); - let mut dev_feats = BTreeMap::new(); - 'dev_dependency: loop { + let mut build_feats = BTreeMap::new(); + 'build_dependency: loop { // DFS traverse of the current member and everything below it - collect_features_from(&mut dfs, fg, &mut dev_feats, Collect::MemberDev(member)); + collect_features_from(&mut dfs, fg, &mut build_feats, Collect::MemberBuild(member)); - dev_feats.retain(|key, _val| filtered_workspace_feats.contains_key(key)); + build_feats.retain(|key, _| { + fg.features[*key] + .fid() + .is_some_and(|fid| fid.instance == CrateInstance::Host) + && filtered_workspace_host_feats.contains_key(key) + }); debug!( - "Accumulated dev deps for {:?} are as following:{}", + "Accumulated build deps for {:?} are as following:{}", member.package().name, - show_detached_dep_tree(&dev_feats, fg), + show_detached_dep_tree(&build_feats, fg), ); - for (&dep, feats) in &dev_feats { - if let Some(ws_feats) = raw_workspace_feats.get(&dep) { - if ws_feats != feats { - if let Some(&missing_feat) = ws_feats.difference(feats).next() { - debug!("\t{member:?} lacks dev {}", fg.features[missing_feat]); - - changed - .entry(member) - .or_insert_with(BTreeMap::default) - .insert((Ty::Dev, dep), ws_feats.clone()); - - let new_dep = - fg.add_edge(member_ix, missing_feat, false, DepKindInfo::DEV)?; - dfs.move_to(new_dep); - - trace!("Performing one more dev iteration on {member:?}"); - continue 'dev_dependency; - } - } + for (&dep, feats) in &build_feats { + if let Some(ws_feats) = filtered_workspace_host_feats.get(&dep) + && ws_feats != feats + && let Some(&missing_feat) = ws_feats.difference(feats).next() + { + debug!("\t{member:?} lacks build {}", fg.features[missing_feat]); + + changed + .entry(member) + .or_insert_with(BTreeMap::default) + .insert((Ty::Build, dep), ws_feats.clone()); + + let new_dep = fg.add_edge( + member_ix, + missing_feat, + false, + DepKindInfo::BUILD, + CrateInstance::Target, + )?; + dfs.move_to(new_dep); + + trace!("Performing one more dev iteration on {member:?}"); + continue 'build_dependency; } } @@ -386,7 +427,7 @@ pub fn get_changeset<'a>(fg: &mut FeatGraph<'a>, no_dev: bool) -> anyhow::Result use std::cell::RefCell; let mut deps = BTreeMap::new(); let cell = RefCell::new(&mut deps); - let package_index = match fg.fid_cache.get(&package.root()) { + let package_index = match fg.fid_cache.get(&package.root(CrateInstance::Target)) { Some(ix) => ix, None => continue, }; @@ -428,11 +469,17 @@ pub fn get_changeset<'a>(fg: &mut FeatGraph<'a>, no_dev: bool) -> anyhow::Result .filter_map(|f| match fg.features[*f].fid()?.dep { Feat::Base => None, Feat::Named(name) => Some(name.to_string()), + // `Feat::Dep` is the node that represents an + // optional dependency being enabled, not a + // feature of the parent crate. Enabling it + // means turning the optional dep on, so it + // doesn't belong in the parent's feature list. + Feat::Dep(_) => None, }) .collect::>(); let rename = renames .get(&pid) - .map_or(false, |names| names.contains(&package.package().name)); + .is_some_and(|names| names.contains(&package.package().name)); Some(FeatChange { pid: package, ty, diff --git a/src/main.rs b/src/main.rs index fb8b5c2..169251c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,18 +8,18 @@ use cargo_hackerman::{ toml, }; use cargo_metadata::camino::Utf8PathBuf; -use cargo_platform::Cfg; +use cargo_metadata::cargo_platform::Cfg; use std::{ collections::{BTreeMap, BTreeSet}, process::Command, str::FromStr, }; use tracing::Level; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; +use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt}; fn start_subscriber((_, level): (usize, Level)) { let filter = EnvFilter::try_from_default_env() - .unwrap_or_else(|_| (EnvFilter::default().add_directive(level.into()))); + .unwrap_or_else(|_| EnvFilter::default().add_directive(level.into())); let fmt_layer = tracing_subscriber::fmt::layer() .with_target(false) .without_time() @@ -43,22 +43,50 @@ fn get_cfgs() -> anyhow::Result> { .collect::, _>>()?) } +fn get_platform( + meta: &cargo_metadata::Metadata, +) -> Result { + use std::borrow::Cow; + use target_spec::*; + + fn read_custom(meta: &cargo_metadata::Metadata) -> Option> { + let hm = meta.workspace_metadata.get("hackerman")?.as_object()?; + hm.get("platform")? + .as_str() + .map(|s| Cow::Owned(s.to_owned())) + } + fn read_features(meta: &cargo_metadata::Metadata) -> Option>> { + let hm = meta.workspace_metadata.get("hackerman")?.as_object()?; + + let zzz = hm + .get("features")? + .as_array()? + .iter() + .filter_map(|s| s.as_str().map(|s| Cow::Owned(s.to_owned()))) + .collect(); + + Some(zzz) + } + + let Some(custom) = read_custom(meta) else { + return Platform::build_target(); + }; + let feats = read_features(meta).unwrap_or_default(); + let feats = target_spec::TargetFeatures::Features(feats); + Platform::new(custom, feats) +} + fn main() -> anyhow::Result<()> { let action = opts::action().fallback_to_usage().run(); match action { - Action::Hack { - profile, - dry, - lock, - no_dev, - } => { + Action::Hack { profile, dry, lock } => { start_subscriber(profile.verbosity); let metadata = profile.exec()?; - let platform = target_spec::Platform::current()?; + let platform = get_platform(&metadata)?; let triplets = vec![platform.triple_str()]; let cfgs = get_cfgs()?; - hack(dry, lock, no_dev, &metadata, triplets, cfgs)?; + hack(dry, lock, &metadata, triplets, cfgs)?; // regenerate Cargo.lock file if !dry { @@ -89,7 +117,7 @@ fn main() -> anyhow::Result<()> { } } - Action::Check { profile, no_dev } => { + Action::Check { profile } => { start_subscriber(profile.verbosity); let metadata = profile.exec()?; let members = metadata.workspace_members.iter().collect::>(); @@ -98,10 +126,10 @@ fn main() -> anyhow::Result<()> { toml::verify_checksum(package.manifest_path.as_std_path())?; } } - let platform = target_spec::Platform::current()?; + let platform = get_platform(&metadata)?; let triplets = vec![platform.triple_str()]; let cfgs = get_cfgs()?; - hack(true, false, no_dev, &metadata, triplets, cfgs)?; + hack(true, false, &metadata, triplets, cfgs)?; } Action::MergeDriver { @@ -125,7 +153,7 @@ fn main() -> anyhow::Result<()> { } => { start_subscriber(profile.verbosity); let metadata = profile.exec()?; - let platform = target_spec::Platform::current()?; + let platform = get_platform(&metadata)?; let triplets = vec![platform.triple_str()]; let cfgs = get_cfgs()?; let mut fg = FeatGraph::init(&metadata, triplets, cfgs)?; @@ -133,7 +161,7 @@ fn main() -> anyhow::Result<()> { tree( &mut fg, krate.as_ref(), - feature.as_ref(), + feature.as_deref(), version.as_ref(), package_nodes, workspace, @@ -153,7 +181,7 @@ fn main() -> anyhow::Result<()> { } => { start_subscriber(profile.verbosity); let metadata = profile.exec()?; - let platform = target_spec::Platform::current()?; + let platform = get_platform(&metadata)?; let triplets = vec![platform.triple_str()]; let cfgs = get_cfgs()?; let mut fg = FeatGraph::init(&metadata, triplets, cfgs)?; @@ -162,7 +190,7 @@ fn main() -> anyhow::Result<()> { explain( &mut fg, &krate, - feature.as_ref(), + feature.as_deref(), version.as_ref(), package_nodes, stdout, @@ -180,10 +208,7 @@ fn main() -> anyhow::Result<()> { .packages .iter() .find(|p| { - p.name == krate - && version - .as_ref() - .map_or(true, |v| &p.version.to_string() == v) + p.name == krate && version.as_ref().is_none_or(|v| &p.version.to_string() == v) }) .ok_or_else(|| anyhow::anyhow!("{krate} {version:?} is not used"))?; @@ -228,7 +253,7 @@ fn main() -> anyhow::Result<()> { Action::Dupes { profile } => { let mut any = false; let metadata = profile.exec()?; - let platform = target_spec::Platform::current()?; + let platform = get_platform(&metadata)?; let triplets = vec![platform.triple_str()]; let cfgs = get_cfgs()?; let mut fg = FeatGraph::init(&metadata, triplets, cfgs)?; @@ -269,7 +294,7 @@ fn open_url(url: &str) -> anyhow::Result<()> { } else if cfg!(target_os = "windows") { Command::new("start").arg(url).output()?; } else { - #[cfg(feature = "webbroser")] + #[cfg(feature = "webbrowser")] { webbrowser::open(url)?; return Ok(()); diff --git a/src/metadata.rs b/src/metadata.rs index 20a18ef..843da0f 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -1,5 +1,5 @@ use cargo_metadata::Dependency; -use cargo_platform::Cfg; +use cargo_metadata::cargo_platform::{Cfg, Platform}; use crate::{feat_graph::Feature, hack::Collect}; @@ -29,7 +29,7 @@ impl From for DependencyKind { #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct DepKindInfo { pub kind: DependencyKind, - pub target: Option, + pub target: Option, } impl DepKindInfo { @@ -43,6 +43,11 @@ impl DepKindInfo { target: None, }; + pub const BUILD: Self = Self { + kind: DependencyKind::Build, + target: None, + }; + fn satisfies( &self, source: Feature, @@ -53,9 +58,9 @@ impl DepKindInfo { if self.kind == DependencyKind::Development { match filter { Collect::AllTargets | Collect::Target | Collect::NoDev | Collect::NormalOnly => { - return false + return false; } - Collect::MemberDev(pid) => { + Collect::MemberBuild(pid) => { if let Some(this_fid) = source.fid() { { if this_fid.pid != pid { @@ -74,7 +79,7 @@ impl DepKindInfo { self.target .as_ref() - .map_or(true, |p| p.matches(platforms[0], cfgs)) + .is_none_or(|p| p.matches(platforms[0], cfgs)) } } diff --git a/src/opts.rs b/src/opts.rs index 1fc19a3..edbdee6 100644 --- a/src/opts.rs +++ b/src/opts.rs @@ -1,4 +1,4 @@ -use bpaf::{doc::Style, positional, short, Bpaf, Parser}; +use bpaf::{Bpaf, Parser, doc::Style, positional, short}; use cargo_metadata::Metadata; use semver::Version; use std::{path::PathBuf, str::FromStr}; @@ -51,10 +51,6 @@ pub enum Action { /// ``` /// lock: bool, - - /// Don't unify dev dependencies - #[bpaf(short('D'), long)] - no_dev: bool, }, /// Remove crate dependency unification added by the `hack` command @@ -76,10 +72,6 @@ pub enum Action { Check { #[bpaf(external(profile))] profile: Profile, - - /// Don't unify dev dependencies - #[bpaf(short('D'), long)] - no_dev: bool, }, /// Restore files and merge with the default merge driver @@ -339,9 +331,9 @@ mod readme { use std::io::Read; use std::io::Seek; let mut file = std::fs::OpenOptions::new() - .write(true) .read(true) .create(true) + .append(true) .open(path)?; let mut current_val = String::new(); file.read_to_string(&mut current_val)?; diff --git a/src/source.rs b/src/source.rs index 9aef65c..ba7c945 100644 --- a/src/source.rs +++ b/src/source.rs @@ -23,7 +23,7 @@ fn optimize_feats(declared: &BTreeMap>, requested: &mut BTre #[cfg(test)] mod tests { - use super::{optimize_feats, PackageSource}; + use super::{PackageSource, optimize_feats}; use std::collections::{BTreeMap, BTreeSet}; fn check(req: &[&str], decl: &[(&str, &[&str])], exp: &[&str]) { @@ -124,7 +124,7 @@ impl<'a> ChangePackage<'a> { if let Some(src) = &package.source { let source = PackageSource::try_from(src.repr.as_str())?; Ok(ChangePackage { - name: package.name.clone(), + name: package.name.as_ref().to_string(), ty, version: package.version.clone(), source, @@ -150,7 +150,7 @@ impl<'a> ChangePackage<'a> { } }; Ok(ChangePackage { - name: package.name.clone(), + name: package.name.as_ref().to_string(), ty, version: package.version.clone(), source, @@ -187,7 +187,18 @@ impl PackageSource<'_> { table.insert("version", toml_edit::Value::from(ver.to_string())); } PackageSource::Git(url) => { - table.insert("git", toml_edit::Value::from(*url)); + if let Some((base, query)) = url.split_once('?') { + table.insert("git", toml_edit::Value::from(base)); + for pair in query.split('&') { + if let Some((key, value)) = pair.split_once('=') + && matches!(key, "rev" | "branch" | "tag") + { + table.insert(key, toml_edit::Value::from(value)); + } + } + } else { + table.insert("git", toml_edit::Value::from(*url)); + } } PackageSource::File { path } => { table.insert("path", toml_edit::Value::from(path.to_string())); diff --git a/src/toml.rs b/src/toml.rs index f7159eb..cc720a8 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -5,7 +5,7 @@ use cargo_metadata::camino::{Utf8Path, Utf8PathBuf}; use std::hash::{Hash, Hasher}; use std::ops::{Index, IndexMut}; use std::path::Path; -use toml_edit::{value, Array, Decor, Document, InlineTable, Item, Table, Value}; +use toml_edit::{Array, Decor, DocumentMut, InlineTable, Item, Table, Value, value}; use tracing::{debug, info}; use crate::hack::Ty; @@ -24,14 +24,14 @@ pub fn set_dependencies( changes: &[ChangePackage], ) -> anyhow::Result<()> { info!("updating {path}"); - let mut toml = std::fs::read_to_string(path)?.parse::()?; + let mut toml = std::fs::read_to_string(path)?.parse::()?; set_dependencies_toml(&mut toml, lock, changes)?; std::fs::write(path, toml.to_string())?; Ok(()) } -fn get_decor(toml: &mut Document) -> anyhow::Result<&mut Decor> { +fn get_decor(toml: &mut DocumentMut) -> anyhow::Result<&mut Decor> { let (_key, item) = toml .as_table_mut() .iter_mut() @@ -49,12 +49,14 @@ fn get_decor(toml: &mut Document) -> anyhow::Result<&mut Decor> { }) } -fn add_banner(toml: &mut Document) -> anyhow::Result<()> { +fn add_banner(toml: &mut DocumentMut) -> anyhow::Result<()> { let decor = get_decor(toml)?; match decor.prefix().and_then(|x| x.as_str()) { Some(old) => { if old.starts_with(BANNER) { - anyhow::bail!("Found an old banner while trying to hack a file. You should restore it first before hacking againt"); + anyhow::bail!( + "Found an old banner while trying to hack a file. You should restore it first before hacking againt" + ); } let new = format!("{BANNER}{old}"); @@ -65,7 +67,7 @@ fn add_banner(toml: &mut Document) -> anyhow::Result<()> { Ok(()) } -fn strip_banner(toml: &mut Document) -> anyhow::Result { +fn strip_banner(toml: &mut DocumentMut) -> anyhow::Result { let decor = get_decor(toml)?; Ok(match decor.prefix().and_then(|x| x.as_str()) { Some(cur) => { @@ -86,7 +88,7 @@ const LOCK_PATH: &[&str] = &["package", "metadata", "hackerman", "lock"]; const STASH_PATH: &[&str] = &["package", "metadata", "hackerman", "stash"]; const NORM_STASH_PATH: &[&str] = &["package", "metadata", "hackerman", "stash", "dependencies"]; #[rustfmt::skip] -const DEV_STASH_PATH: &[&str] = &["package", "metadata", "hackerman", "stash", "dev-dependencies"]; +const BUILD_STASH_PATH: &[&str] = &["package", "metadata", "hackerman", "stash", "build-dependencies"]; fn get_table<'a>(mut table: &'a mut Table, path: &[&str]) -> anyhow::Result<&'a mut Table> { for (ix, comp) in path.iter().enumerate() { @@ -122,7 +124,7 @@ fn add_checksum(item: &Item, hasher: &mut H) -> anyhow::Result<()> { Ok(()) } -fn get_checksum(toml: &Document) -> anyhow::Result { +fn get_checksum(toml: &DocumentMut) -> anyhow::Result { let mut hasher = std::collections::hash_map::DefaultHasher::new(); let t = match toml.as_item() { @@ -176,7 +178,7 @@ fn compile_change_package(change: &ChangePackage) -> (Item, String) { #[derive(Default)] struct Stash { norm: Vec<(String, Item)>, - dev: Vec<(String, Item)>, + build: Vec<(String, Item)>, } impl Index for Stash { @@ -184,7 +186,7 @@ impl Index for Stash { fn index(&self, index: Ty) -> &Self::Output { match index { - Ty::Dev => &self.dev, + Ty::Build => &self.build, Ty::Norm => &self.norm, } } @@ -193,20 +195,22 @@ impl Index for Stash { impl IndexMut for Stash { fn index_mut(&mut self, index: Ty) -> &mut Self::Output { match index { - Ty::Dev => &mut self.dev, + Ty::Build => &mut self.build, Ty::Norm => &mut self.norm, } } } fn set_dependencies_toml( - toml: &mut Document, + toml: &mut DocumentMut, lock: bool, changes: &[ChangePackage], ) -> anyhow::Result { let mut was_modified = false; if toml.contains_key("target") { - anyhow::bail!("target filtered dependencies present in the workspace are not supported by split mode hack") + anyhow::bail!( + "target filtered dependencies present in the workspace are not supported by split mode hack" + ) } let mut saved = Stash::default(); @@ -217,7 +221,7 @@ fn set_dependencies_toml( let old = table.insert(&name, item).unwrap_or_else(|| value(false)); saved[change.ty].push((name, old)); } - for &ty in &[Ty::Norm, Ty::Dev] { + for &ty in &[Ty::Norm, Ty::Build] { if !saved[ty].is_empty() { get_table(toml, &[ty.table_name()])?.sort_values(); } @@ -229,23 +233,23 @@ fn set_dependencies_toml( let lock_table = get_table(toml, LOCK_PATH)?; lock_table.insert("dependencies", value(hash)); lock_table.sort_values(); - lock_table.set_position(997); + lock_table.set_position(Some(997)); } let stash = get_table(toml, NORM_STASH_PATH)?; - stash.set_position(998); + stash.set_position(Some(998)); for (name, val) in saved.norm { stash.insert(&name, val); } stash.sort_values(); - let dev_stash = get_table(toml, DEV_STASH_PATH)?; - dev_stash.set_position(999); - for (name, val) in saved.dev { - dev_stash.insert(&name, val); + let build_stash = get_table(toml, BUILD_STASH_PATH)?; + build_stash.set_position(Some(999)); + for (name, val) in saved.build { + build_stash.insert(&name, val); } - dev_stash.sort_values(); + build_stash.sort_values(); if was_modified { add_banner(toml)?; } @@ -253,7 +257,7 @@ fn set_dependencies_toml( } pub fn restore_path(manifest_path: &Path) -> anyhow::Result { - let mut toml = std::fs::read_to_string(manifest_path)?.parse::()?; + let mut toml = std::fs::read_to_string(manifest_path)?.parse::()?; let changed = restore_toml(&mut toml)?; if changed { std::fs::write(manifest_path, toml.to_string())?; @@ -262,7 +266,7 @@ pub fn restore_path(manifest_path: &Path) -> anyhow::Result { } pub fn restore(manifest_path: &Utf8Path) -> anyhow::Result { - let mut toml = std::fs::read_to_string(manifest_path)?.parse::()?; + let mut toml = std::fs::read_to_string(manifest_path)?.parse::()?; info!("Restoring {manifest_path}"); let changed = restore_toml(&mut toml).with_context(|| format!("in {manifest_path}"))?; @@ -275,7 +279,7 @@ pub fn restore(manifest_path: &Utf8Path) -> anyhow::Result { Ok(changed) } -fn restore_toml(toml: &mut Document) -> anyhow::Result { +fn restore_toml(toml: &mut DocumentMut) -> anyhow::Result { let hackerman = get_table(toml, HACKERMAN_PATH)?; let mut changed = hackerman.remove("lock").is_some(); @@ -306,7 +310,7 @@ fn restore_toml(toml: &mut Document) -> anyhow::Result { } pub fn verify_checksum(manifest_path: &Path) -> anyhow::Result<()> { - let mut toml = std::fs::read_to_string(manifest_path)?.parse::()?; + let mut toml = std::fs::read_to_string(manifest_path)?.parse::()?; let checksum = get_checksum(&toml)?; @@ -314,11 +318,7 @@ pub fn verify_checksum(manifest_path: &Path) -> anyhow::Result<()> { if lock_table.is_empty() { return Ok(()); } - if lock_table - .get("dependencies") - .and_then(Item::as_integer) - .map_or(false, |l| l == checksum) - { + if lock_table.get("dependencies").and_then(Item::as_integer) == Some(checksum) { anyhow::bail!("Checksum mismatch in {manifest_path:?}") } @@ -341,7 +341,7 @@ mod tests { [target.'cfg(target_os = "android")'.dependencies] package = 1.0 "# - .parse::()?; + .parse::()?; let hash = get_checksum(&toml)?; assert_eq!(hash, 2329902156198620770); @@ -356,7 +356,7 @@ by_version_1 = "1.0" by_version_2 = { version = "1.0", features = ["one", "two"] } from_git = { git = "https://github.com/rust-lang/regex" } "# - .parse::()?; + .parse::()?; let hash = get_checksum(&toml)?; @@ -410,7 +410,7 @@ version = 1.0 [dependencies] package = 1.0 "# - .parse::()?; + .parse::()?; let mut feats = BTreeSet::new(); feats.insert("dummy".to_string()); @@ -440,30 +440,30 @@ package = 1.0 Ok(()) } /* - #[test] - fn set_dependencies_works_1() -> anyhow::Result<()> { - let mut toml = r#" + #[test] + fn set_dependencies_works_1() -> anyhow::Result<()> { + let mut toml = r#" [target.'cfg(target_os = "linux")'.dependencies] package = 1.0 "# - .parse::()?; + .parse::()?; - let mut feats = BTreeSet::new(); - feats.insert("dummy".to_string()); + let mut feats = BTreeSet::new(); + feats.insert("dummy".to_string()); - let changes = [ChangePackage { - name: "package".to_string(), - ty: Ty::Norm, - version: Version::new(1, 0, 0), - source: PackageSource::CRATES_IO, - feats, - rename: false, - }]; + let changes = [ChangePackage { + name: "package".to_string(), + ty: Ty::Norm, + version: Version::new(1, 0, 0), + source: PackageSource::CRATES_IO, + feats, + rename: false, + }]; - set_dependencies_toml(&mut toml, false, &changes)?; + set_dependencies_toml(&mut toml, false, &changes)?; - todo!("{toml}"); + todo!("{toml}"); - Ok(()) - }*/ + Ok(()) + }*/ } diff --git a/test_workspaces/10/Cargo.lock b/test_workspaces/10/Cargo.lock deleted file mode 100644 index 1434418..0000000 --- a/test_workspaces/10/Cargo.lock +++ /dev/null @@ -1,14 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "alpha" -version = "0.1.0" -dependencies = [ - "beta", -] - -[[package]] -name = "beta" -version = "0.1.0" diff --git a/test_workspaces/10/Cargo.toml b/test_workspaces/10/Cargo.toml deleted file mode 100644 index be0064e..0000000 --- a/test_workspaces/10/Cargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[workspace] -members = ["alpha"] diff --git a/test_workspaces/10/alpha/Cargo.toml b/test_workspaces/10/alpha/Cargo.toml deleted file mode 100644 index 9247507..0000000 --- a/test_workspaces/10/alpha/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "alpha" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -beta = { path = "../../10a/beta", optional = true, default-features = false } - -# optional dependency link should obey default-features = false -[features] -default = ["dep:beta"] diff --git a/test_workspaces/10/alpha/src/lib.rs b/test_workspaces/10/alpha/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/10/alpha/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/10a/beta/Cargo.toml b/test_workspaces/10a/beta/Cargo.toml deleted file mode 100644 index 47f4c22..0000000 --- a/test_workspaces/10a/beta/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "beta" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[features] -default = ["one"] -one = [] - diff --git a/test_workspaces/10a/beta/src/lib.rs b/test_workspaces/10a/beta/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/10a/beta/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/2/Cargo.lock b/test_workspaces/2/Cargo.lock deleted file mode 100644 index 1434418..0000000 --- a/test_workspaces/2/Cargo.lock +++ /dev/null @@ -1,14 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "alpha" -version = "0.1.0" -dependencies = [ - "beta", -] - -[[package]] -name = "beta" -version = "0.1.0" diff --git a/test_workspaces/2/Cargo.toml b/test_workspaces/2/Cargo.toml deleted file mode 100644 index aeb2810..0000000 --- a/test_workspaces/2/Cargo.toml +++ /dev/null @@ -1 +0,0 @@ -workspace.members = ["alpha"] diff --git a/test_workspaces/2/alpha/Cargo.toml b/test_workspaces/2/alpha/Cargo.toml deleted file mode 100644 index eb0879c..0000000 --- a/test_workspaces/2/alpha/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "alpha" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -gamma = { path = "../beta/", package = "beta", optional = true } - -[features] -debug = ["gamma/debug"] diff --git a/test_workspaces/2/alpha/src/lib.rs b/test_workspaces/2/alpha/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/2/alpha/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/2/beta/Cargo.toml b/test_workspaces/2/beta/Cargo.toml deleted file mode 100644 index 98bc2f8..0000000 --- a/test_workspaces/2/beta/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "beta" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[features] -debug = [] diff --git a/test_workspaces/2/beta/src/lib.rs b/test_workspaces/2/beta/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/2/beta/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/2/metadata.json b/test_workspaces/2/metadata.json deleted file mode 100644 index 8dcd406..0000000 --- a/test_workspaces/2/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"packages":[{"name":"alpha","version":"0.1.0","id":"alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/2/alpha)","license":null,"license_file":null,"description":null,"source":null,"dependencies":[{"name":"beta","source":null,"req":"*","kind":null,"rename":"gamma","optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null,"path":"/home/pacak/ej/cargo-hackerman/demo/2/beta"}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"alpha","src_path":"/home/pacak/ej/cargo-hackerman/demo/2/alpha/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"debug":["gamma/debug"],"gamma":["dep:gamma"]},"manifest_path":"/home/pacak/ej/cargo-hackerman/demo/2/alpha/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":[],"keywords":[],"readme":null,"repository":null,"homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"beta","version":"0.1.0","id":"beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/2/beta)","license":null,"license_file":null,"description":null,"source":null,"dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"beta","src_path":"/home/pacak/ej/cargo-hackerman/demo/2/beta/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"debug":[]},"manifest_path":"/home/pacak/ej/cargo-hackerman/demo/2/beta/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":[],"keywords":[],"readme":null,"repository":null,"homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null}],"workspace_members":["alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/2/alpha)","beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/2/beta)"],"resolve":{"nodes":[{"id":"alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/2/alpha)","dependencies":[],"deps":[],"features":[]},{"id":"beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/2/beta)","dependencies":[],"deps":[],"features":[]}],"root":"alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/2/alpha)"},"target_directory":"/home/pacak/ej/cargo-hackerman/demo/2/target","version":1,"workspace_root":"/home/pacak/ej/cargo-hackerman/demo/2","metadata":null} diff --git a/test_workspaces/3/Cargo.lock b/test_workspaces/3/Cargo.lock deleted file mode 100644 index 317247e..0000000 --- a/test_workspaces/3/Cargo.lock +++ /dev/null @@ -1,14 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "alpha" -version = "0.1.0" -dependencies = [ - "beta-na-me", -] - -[[package]] -name = "beta-na-me" -version = "0.1.0" diff --git a/test_workspaces/3/Cargo.toml b/test_workspaces/3/Cargo.toml deleted file mode 100644 index aeb2810..0000000 --- a/test_workspaces/3/Cargo.toml +++ /dev/null @@ -1 +0,0 @@ -workspace.members = ["alpha"] diff --git a/test_workspaces/3/alpha/Cargo.toml b/test_workspaces/3/alpha/Cargo.toml deleted file mode 100644 index addf99d..0000000 --- a/test_workspaces/3/alpha/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "alpha" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -beta-na-me = { path = "../beta-na_me/", optional = true } - -[features] -debug = ["beta-na-me/debug"] diff --git a/test_workspaces/3/alpha/src/lib.rs b/test_workspaces/3/alpha/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/3/alpha/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/3/beta-na_me/Cargo.toml b/test_workspaces/3/beta-na_me/Cargo.toml deleted file mode 100644 index e938028..0000000 --- a/test_workspaces/3/beta-na_me/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "beta-na-me" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[features] -debug = [] diff --git a/test_workspaces/3/beta-na_me/src/lib.rs b/test_workspaces/3/beta-na_me/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/3/beta-na_me/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/3/metadata.json b/test_workspaces/3/metadata.json deleted file mode 100644 index 641bd75..0000000 --- a/test_workspaces/3/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"packages":[{"name":"alpha","version":"0.1.0","id":"alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/3/alpha)","license":null,"license_file":null,"description":null,"source":null,"dependencies":[{"name":"beta-na-me","source":null,"req":"*","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null,"path":"/home/pacak/ej/cargo-hackerman/demo/3/beta-na_me"}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"alpha","src_path":"/home/pacak/ej/cargo-hackerman/demo/3/alpha/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"beta-na-me":["dep:beta-na-me"],"debug":["beta-na-me/debug"]},"manifest_path":"/home/pacak/ej/cargo-hackerman/demo/3/alpha/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":[],"keywords":[],"readme":null,"repository":null,"homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"beta-na-me","version":"0.1.0","id":"beta-na-me 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/3/beta-na_me)","license":null,"license_file":null,"description":null,"source":null,"dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"beta-na-me","src_path":"/home/pacak/ej/cargo-hackerman/demo/3/beta-na_me/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"debug":[]},"manifest_path":"/home/pacak/ej/cargo-hackerman/demo/3/beta-na_me/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":[],"keywords":[],"readme":null,"repository":null,"homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null}],"workspace_members":["alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/3/alpha)","beta-na-me 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/3/beta-na_me)"],"resolve":{"nodes":[{"id":"alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/3/alpha)","dependencies":[],"deps":[],"features":[]},{"id":"beta-na-me 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/3/beta-na_me)","dependencies":[],"deps":[],"features":[]}],"root":"alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/3/alpha)"},"target_directory":"/home/pacak/ej/cargo-hackerman/demo/3/target","version":1,"workspace_root":"/home/pacak/ej/cargo-hackerman/demo/3","metadata":null} diff --git a/test_workspaces/4/Cargo.lock b/test_workspaces/4/Cargo.lock deleted file mode 100644 index 717f96a..0000000 --- a/test_workspaces/4/Cargo.lock +++ /dev/null @@ -1,17 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "alpha" -version = "0.1.0" -dependencies = [ - "beta", -] - -[[package]] -name = "beta" -version = "0.1.0" -dependencies = [ - "beta", -] diff --git a/test_workspaces/4/Cargo.toml b/test_workspaces/4/Cargo.toml deleted file mode 100644 index aeb2810..0000000 --- a/test_workspaces/4/Cargo.toml +++ /dev/null @@ -1 +0,0 @@ -workspace.members = ["alpha"] diff --git a/test_workspaces/4/alpha/Cargo.toml b/test_workspaces/4/alpha/Cargo.toml deleted file mode 100644 index e3cce83..0000000 --- a/test_workspaces/4/alpha/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "alpha" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - - - -[target.'cfg(target_family = "unix")'.dependencies] -beta = { path = "../beta", features = ["unix"] } -[target.'cfg(target_family = "windows")'.dependencies] -beta = { path = "../beta", features = ["windows"] } - -[features] -debug = ["beta/debug"] diff --git a/test_workspaces/4/alpha/src/lib.rs b/test_workspaces/4/alpha/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/4/alpha/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/4/beta/Cargo.toml b/test_workspaces/4/beta/Cargo.toml deleted file mode 100644 index 8e627de..0000000 --- a/test_workspaces/4/beta/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "beta" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[dev-dependencies] -beta = { path = ".", features = ["debug"] } - -[features] -debug = [] -unix = [] -windows = [] diff --git a/test_workspaces/4/beta/src/lib.rs b/test_workspaces/4/beta/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/4/beta/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/4/metadata.json b/test_workspaces/4/metadata.json deleted file mode 100644 index 31291a2..0000000 --- a/test_workspaces/4/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"packages":[{"name":"alpha","version":"0.1.0","id":"alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/4/alpha)","license":null,"license_file":null,"description":null,"source":null,"dependencies":[{"name":"beta","source":null,"req":"*","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["unix"],"target":"cfg(target_family = \"unix\")","registry":null,"path":"/home/pacak/ej/cargo-hackerman/demo/4/beta"},{"name":"beta","source":null,"req":"*","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":["windows"],"target":"cfg(target_family = \"windows\")","registry":null,"path":"/home/pacak/ej/cargo-hackerman/demo/4/beta"}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"alpha","src_path":"/home/pacak/ej/cargo-hackerman/demo/4/alpha/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"debug":["beta/debug"]},"manifest_path":"/home/pacak/ej/cargo-hackerman/demo/4/alpha/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":[],"keywords":[],"readme":null,"repository":null,"homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"beta","version":"0.1.0","id":"beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/4/beta)","license":null,"license_file":null,"description":null,"source":null,"dependencies":[{"name":"beta","source":null,"req":"*","kind":"dev","rename":null,"optional":false,"uses_default_features":true,"features":["debug"],"target":null,"registry":null,"path":"/home/pacak/ej/cargo-hackerman/demo/4/beta"}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"beta","src_path":"/home/pacak/ej/cargo-hackerman/demo/4/beta/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"debug":[],"unix":[],"windows":[]},"manifest_path":"/home/pacak/ej/cargo-hackerman/demo/4/beta/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":[],"keywords":[],"readme":null,"repository":null,"homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null}],"workspace_members":["alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/4/alpha)","beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/4/beta)"],"resolve":{"nodes":[{"id":"alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/4/alpha)","dependencies":["beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/4/beta)"],"deps":[{"name":"beta","pkg":"beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/4/beta)","dep_kinds":[{"kind":null,"target":"cfg(target_family = \"unix\")"},{"kind":null,"target":"cfg(target_family = \"windows\")"}]}],"features":[]},{"id":"beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/4/beta)","dependencies":["beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/4/beta)"],"deps":[{"name":"beta","pkg":"beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/4/beta)","dep_kinds":[{"kind":"dev","target":null}]}],"features":["debug","unix","windows"]}],"root":"alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/4/alpha)"},"target_directory":"/home/pacak/ej/cargo-hackerman/demo/4/target","version":1,"workspace_root":"/home/pacak/ej/cargo-hackerman/demo/4","metadata":null} diff --git a/test_workspaces/5/Cargo.lock b/test_workspaces/5/Cargo.lock deleted file mode 100644 index 918ec9b..0000000 --- a/test_workspaces/5/Cargo.lock +++ /dev/null @@ -1,22 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "alpha" -version = "0.1.0" -dependencies = [ - "beta", - "gamma", -] - -[[package]] -name = "beta" -version = "0.1.0" -dependencies = [ - "gamma", -] - -[[package]] -name = "gamma" -version = "0.1.0" diff --git a/test_workspaces/5/Cargo.toml b/test_workspaces/5/Cargo.toml deleted file mode 100644 index 4d4722c..0000000 --- a/test_workspaces/5/Cargo.toml +++ /dev/null @@ -1 +0,0 @@ -workspace.members = ["alpha", "beta", "gamma"] diff --git a/test_workspaces/5/alpha/Cargo.toml b/test_workspaces/5/alpha/Cargo.toml deleted file mode 100644 index 2ffc201..0000000 --- a/test_workspaces/5/alpha/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "alpha" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -beta = { path = "../beta" } -gamma = { path = "../gamma", optional = true } - - -[features] -one = ["dep:gamma", "gamma?/one"] -default = ["one"] diff --git a/test_workspaces/5/alpha/src/lib.rs b/test_workspaces/5/alpha/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/5/alpha/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/5/beta/Cargo.toml b/test_workspaces/5/beta/Cargo.toml deleted file mode 100644 index d745665..0000000 --- a/test_workspaces/5/beta/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "beta" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -gamma = { path = "../gamma" } diff --git a/test_workspaces/5/beta/src/lib.rs b/test_workspaces/5/beta/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/5/beta/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/5/gamma/Cargo.toml b/test_workspaces/5/gamma/Cargo.toml deleted file mode 100644 index 1308f0f..0000000 --- a/test_workspaces/5/gamma/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "gamma" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[features] -one = [] diff --git a/test_workspaces/5/gamma/src/lib.rs b/test_workspaces/5/gamma/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/5/gamma/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/5/metadata.json b/test_workspaces/5/metadata.json deleted file mode 100644 index 8dd51a6..0000000 --- a/test_workspaces/5/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"packages":[{"name":"alpha","version":"0.1.0","id":"alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/alpha)","license":null,"license_file":null,"description":null,"source":null,"dependencies":[{"name":"beta","source":null,"req":"*","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null,"path":"/home/pacak/ej/cargo-hackerman/demo/5/beta"},{"name":"gamma","source":null,"req":"*","kind":null,"rename":null,"optional":true,"uses_default_features":true,"features":[],"target":null,"registry":null,"path":"/home/pacak/ej/cargo-hackerman/demo/5/gamma"}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"alpha","src_path":"/home/pacak/ej/cargo-hackerman/demo/5/alpha/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"default":["one"],"one":["dep:gamma","gamma?/one"]},"manifest_path":"/home/pacak/ej/cargo-hackerman/demo/5/alpha/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":[],"keywords":[],"readme":null,"repository":null,"homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"beta","version":"0.1.0","id":"beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/beta)","license":null,"license_file":null,"description":null,"source":null,"dependencies":[{"name":"gamma","source":null,"req":"*","kind":null,"rename":null,"optional":false,"uses_default_features":true,"features":[],"target":null,"registry":null,"path":"/home/pacak/ej/cargo-hackerman/demo/5/gamma"}],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"beta","src_path":"/home/pacak/ej/cargo-hackerman/demo/5/beta/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{},"manifest_path":"/home/pacak/ej/cargo-hackerman/demo/5/beta/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":[],"keywords":[],"readme":null,"repository":null,"homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null},{"name":"gamma","version":"0.1.0","id":"gamma 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/gamma)","license":null,"license_file":null,"description":null,"source":null,"dependencies":[],"targets":[{"kind":["lib"],"crate_types":["lib"],"name":"gamma","src_path":"/home/pacak/ej/cargo-hackerman/demo/5/gamma/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true}],"features":{"one":[]},"manifest_path":"/home/pacak/ej/cargo-hackerman/demo/5/gamma/Cargo.toml","metadata":null,"publish":null,"authors":[],"categories":[],"keywords":[],"readme":null,"repository":null,"homepage":null,"documentation":null,"edition":"2021","links":null,"default_run":null,"rust_version":null}],"workspace_members":["alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/alpha)","beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/beta)","gamma 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/gamma)"],"resolve":{"nodes":[{"id":"alpha 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/alpha)","dependencies":["beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/beta)","gamma 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/gamma)"],"deps":[{"name":"beta","pkg":"beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/beta)","dep_kinds":[{"kind":null,"target":null}]},{"name":"gamma","pkg":"gamma 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/gamma)","dep_kinds":[{"kind":null,"target":null}]}],"features":["default","one"]},{"id":"beta 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/beta)","dependencies":["gamma 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/gamma)"],"deps":[{"name":"gamma","pkg":"gamma 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/gamma)","dep_kinds":[{"kind":null,"target":null}]}],"features":[]},{"id":"gamma 0.1.0 (path+file:///home/pacak/ej/cargo-hackerman/demo/5/gamma)","dependencies":[],"deps":[],"features":["one"]}],"root":null},"target_directory":"/home/pacak/ej/cargo-hackerman/demo/5/target","version":1,"workspace_root":"/home/pacak/ej/cargo-hackerman/demo/5","metadata":null} diff --git a/test_workspaces/6/Cargo.lock b/test_workspaces/6/Cargo.lock deleted file mode 100644 index 51b7f0d..0000000 --- a/test_workspaces/6/Cargo.lock +++ /dev/null @@ -1,21 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "alpha" -version = "0.1.0" -dependencies = [ - "gamma", -] - -[[package]] -name = "beta" -version = "0.1.0" -dependencies = [ - "gamma", -] - -[[package]] -name = "gamma" -version = "0.1.0" diff --git a/test_workspaces/6/Cargo.toml b/test_workspaces/6/Cargo.toml deleted file mode 100644 index 1a94892..0000000 --- a/test_workspaces/6/Cargo.toml +++ /dev/null @@ -1 +0,0 @@ -workspace.members = ["alpha", "beta"] diff --git a/test_workspaces/6/alpha/Cargo.toml b/test_workspaces/6/alpha/Cargo.toml deleted file mode 100644 index 5d3dc2d..0000000 --- a/test_workspaces/6/alpha/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "alpha" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -gamma = { path = "../gamma" } - -[package.metadata.hackerman.stash.dependencies] -gamma = { path = "../gamma/", default-features = false, features = ["one"] } diff --git a/test_workspaces/6/alpha/src/lib.rs b/test_workspaces/6/alpha/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/6/alpha/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/6/beta/Cargo.toml b/test_workspaces/6/beta/Cargo.toml deleted file mode 100644 index 7a0024c..0000000 --- a/test_workspaces/6/beta/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "beta" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -gamma = { path = "../gamma/" } diff --git a/test_workspaces/6/beta/src/lib.rs b/test_workspaces/6/beta/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/6/beta/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/6/gamma/Cargo.toml b/test_workspaces/6/gamma/Cargo.toml deleted file mode 100644 index 966c45e..0000000 --- a/test_workspaces/6/gamma/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "gamma" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[features] -default = ["one", "two"] -one = [] -two = [] diff --git a/test_workspaces/6/gamma/src/lib.rs b/test_workspaces/6/gamma/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/6/gamma/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/7/Cargo.lock b/test_workspaces/7/Cargo.lock deleted file mode 100644 index 918ec9b..0000000 --- a/test_workspaces/7/Cargo.lock +++ /dev/null @@ -1,22 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "alpha" -version = "0.1.0" -dependencies = [ - "beta", - "gamma", -] - -[[package]] -name = "beta" -version = "0.1.0" -dependencies = [ - "gamma", -] - -[[package]] -name = "gamma" -version = "0.1.0" diff --git a/test_workspaces/7/Cargo.toml b/test_workspaces/7/Cargo.toml deleted file mode 100644 index 1a94892..0000000 --- a/test_workspaces/7/Cargo.toml +++ /dev/null @@ -1 +0,0 @@ -workspace.members = ["alpha", "beta"] diff --git a/test_workspaces/7/alpha/Cargo.toml b/test_workspaces/7/alpha/Cargo.toml deleted file mode 100644 index f9bf381..0000000 --- a/test_workspaces/7/alpha/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "alpha" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -beta = { path = "../beta" } -gamma = { path = "../gamma/", default-features = false } diff --git a/test_workspaces/7/alpha/src/lib.rs b/test_workspaces/7/alpha/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/7/alpha/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/7/beta/Cargo.toml b/test_workspaces/7/beta/Cargo.toml deleted file mode 100644 index 362de4d..0000000 --- a/test_workspaces/7/beta/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "beta" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -gamma = { path = "../gamma/", optional = true, features = ["one", "two"] } - -[features] -default = ["gamma"] diff --git a/test_workspaces/7/beta/src/lib.rs b/test_workspaces/7/beta/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/7/beta/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/7/gamma/Cargo.toml b/test_workspaces/7/gamma/Cargo.toml deleted file mode 100644 index 4b5ac99..0000000 --- a/test_workspaces/7/gamma/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "gamma" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[features] -default = ["one"] -one = [] -two = [] diff --git a/test_workspaces/7/gamma/src/lib.rs b/test_workspaces/7/gamma/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/7/gamma/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/8/Cargo.lock b/test_workspaces/8/Cargo.lock deleted file mode 100644 index c90669a..0000000 --- a/test_workspaces/8/Cargo.lock +++ /dev/null @@ -1,29 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "alpha" -version = "0.1.0" -dependencies = [ - "delta", - "gamma", -] - -[[package]] -name = "beta" -version = "0.1.0" -dependencies = [ - "delta", -] - -[[package]] -name = "delta" -version = "0.1.0" - -[[package]] -name = "gamma" -version = "0.1.0" -dependencies = [ - "delta", -] diff --git a/test_workspaces/8/Cargo.toml b/test_workspaces/8/Cargo.toml deleted file mode 100644 index 3243913..0000000 --- a/test_workspaces/8/Cargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[workspace] -members = ["alpha", "beta"] diff --git a/test_workspaces/8/alpha/Cargo.toml b/test_workspaces/8/alpha/Cargo.toml deleted file mode 100644 index 0b16bec..0000000 --- a/test_workspaces/8/alpha/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "alpha" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -# both import delta with one feature -# one conditionally imports gamma -# gamma imports delta with two features - -[dependencies] -delta = { path = "../../8a/delta" } - -[target.'cfg(target_os = "android")'.dependencies] -gamma = { path = "../../8a/gamma" } diff --git a/test_workspaces/8/alpha/src/lib.rs b/test_workspaces/8/alpha/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/8/alpha/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/8/beta/Cargo.toml b/test_workspaces/8/beta/Cargo.toml deleted file mode 100644 index 8df5f3d..0000000 --- a/test_workspaces/8/beta/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "beta" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -delta = { path = "../../8a/delta" } - -#[target.'cfg(target_os = "android")'.dependencies] -#gamma = { path = "../../8a/gamma" } diff --git a/test_workspaces/8/beta/src/lib.rs b/test_workspaces/8/beta/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/8/beta/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/8a/delta/Cargo.toml b/test_workspaces/8a/delta/Cargo.toml deleted file mode 100644 index 7b65817..0000000 --- a/test_workspaces/8a/delta/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "delta" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[features] -one = [] -two = [] diff --git a/test_workspaces/8a/delta/src/lib.rs b/test_workspaces/8a/delta/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/8a/delta/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/8a/gamma/Cargo.toml b/test_workspaces/8a/gamma/Cargo.toml deleted file mode 100644 index b576726..0000000 --- a/test_workspaces/8a/gamma/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "gamma" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -delta = { path = "../delta", features = ["one", "two"] } diff --git a/test_workspaces/8a/gamma/src/lib.rs b/test_workspaces/8a/gamma/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/8a/gamma/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/9/alpha/Cargo.lock b/test_workspaces/9/alpha/Cargo.lock deleted file mode 100644 index 1434418..0000000 --- a/test_workspaces/9/alpha/Cargo.lock +++ /dev/null @@ -1,14 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "alpha" -version = "0.1.0" -dependencies = [ - "beta", -] - -[[package]] -name = "beta" -version = "0.1.0" diff --git a/test_workspaces/9/alpha/Cargo.toml b/test_workspaces/9/alpha/Cargo.toml deleted file mode 100644 index d633893..0000000 --- a/test_workspaces/9/alpha/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "alpha" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[target.'cfg(target_os = "android")'.dependencies] -beta = { path = "../beta", optional = true } - -[features] -default = ["beta"] diff --git a/test_workspaces/9/alpha/src/lib.rs b/test_workspaces/9/alpha/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/9/alpha/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/9/beta/Cargo.toml b/test_workspaces/9/beta/Cargo.toml deleted file mode 100644 index 0e5befc..0000000 --- a/test_workspaces/9/beta/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "beta" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/test_workspaces/9/beta/src/lib.rs b/test_workspaces/9/beta/src/lib.rs deleted file mode 100644 index 1b4a90c..0000000 --- a/test_workspaces/9/beta/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/test_workspaces/mk.sh b/test_workspaces/mk.sh deleted file mode 100755 index a4f3349..0000000 --- a/test_workspaces/mk.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -ue - - -#cargo metadata --manifest-path 1/Cargo.toml --format-version 1 > 1/metadata.json -#cargo metadata --manifest-path 2/alpha/Cargo.toml --format-version 1 > 2/metadata.json -#cargo metadata --manifest-path 3/alpha/Cargo.toml --format-version 1 > 3/metadata.json -#cargo metadata --manifest-path 4/alpha/Cargo.toml --format-version 1 > 4/metadata.json -cargo metadata --manifest-path 5/Cargo.toml --format-version 1 > 5/metadata.json - diff --git a/workspace-gen/Cargo.lock b/workspace-gen/Cargo.lock new file mode 100644 index 0000000..cf0de17 --- /dev/null +++ b/workspace-gen/Cargo.lock @@ -0,0 +1,180 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "bpaf" +version = "0.9.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b86829876e7e200161a5aa6ea688d46c32d64d70ee3100127790dde84688d6e" +dependencies = [ + "bpaf_derive", +] + +[[package]] +name = "bpaf_derive" +version = "0.5.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f7e98cee839b19076cb3ce1afdb62bb182e04ff5f71f70188827002fae91094" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" + +[[package]] +name = "workspace-gen" +version = "0.1.0" +dependencies = [ + "anyhow", + "bpaf", + "serde", + "toml", +] diff --git a/workspace-gen/Cargo.toml b/workspace-gen/Cargo.toml new file mode 100644 index 0000000..64df2f2 --- /dev/null +++ b/workspace-gen/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "workspace-gen" +version = "0.1.0" +edition = "2024" +description = "Generate cargo workspaces for testing purposes" + +[dependencies] +anyhow = "1" +bpaf = { version = "0.9", features = ["derive"] } +toml = "1.0" +serde = { version = "1", features = ["derive"] } diff --git a/workspace-gen/examples/build_conflict.toml b/workspace-gen/examples/build_conflict.toml new file mode 100644 index 0000000..ed74b63 --- /dev/null +++ b/workspace-gen/examples/build_conflict.toml @@ -0,0 +1,34 @@ +# Workspace C: Host vs Target Dependency Isolation +# +# Tests: host (build) dependencies are NOT unified with target (normal) dependencies + +[workspace] +name = "build_conflict" + +[[crates]] +name = "codec" +features = { decode = [], encode = [], optimize = [], test = [], serialize = [] } +workspace_member = false + +[[crates]] +name = "pass1" +dependencies = { codec = ["encode"] } +workspace_member = false + +[[crates]] +name = "pass2" +features = { } +dependencies = { codec = ["decode"] } +workspace_member = false + +[[crates]] +name = "alpha" +dependencies = { codec = ["decode"] } +build_dependencies = { pass1 = {} } +workspace_member = true + +[[crates]] +name = "beta" +dependencies = { codec = ["optimize"] } +build_dependencies = { pass2 = {}} +workspace_member = true diff --git a/workspace-gen/examples/build_conflict_simple.toml b/workspace-gen/examples/build_conflict_simple.toml new file mode 100644 index 0000000..f89fc21 --- /dev/null +++ b/workspace-gen/examples/build_conflict_simple.toml @@ -0,0 +1,32 @@ +# Workspace C: Host vs Target Dependency Isolation +# +# Tests: host (build) dependencies are NOT unified with target (normal) dependencies + +[workspace] +name = "build_conflict_simple" + +[[crates]] +name = "codec" +features = { decode = [], encode = [] } +workspace_member = false + +[[crates]] +name = "pass1" +dependencies = { codec = ["encode"] } +workspace_member = false + +[[crates]] +name = "pass2" +features = { } +dependencies = { codec = ["decode"] } +workspace_member = false + +[[crates]] +name = "alpha" +build_dependencies = { pass1 = {} } +workspace_member = true + +[[crates]] +name = "beta" +build_dependencies = { pass2 = {}} +workspace_member = true diff --git a/workspace-gen/examples/build_transitive.toml b/workspace-gen/examples/build_transitive.toml new file mode 100644 index 0000000..21d346f --- /dev/null +++ b/workspace-gen/examples/build_transitive.toml @@ -0,0 +1,48 @@ +# Workspace: Transitive Build Dependency Feature Mismatch +# +# Tests: build dependencies that are transitively reachable through normal +# dependency chains should be detected when features differ across members. +# +# Structure: +# alpha (member) --normal--> lib_sqlite --BUILD--> cc (no "parallel") +# beta (member) --normal--> lib_aws --BUILD--> cmake_wrap --normal--> cc (with "parallel") +# +# The workspace resolves cc with "parallel" (from beta's chain). +# But when checking alpha, the build dependency phase is skipped (no direct +# build deps on alpha) and the normal phase doesn't follow BUILD edges, +# so cc's missing "parallel" feature is never detected. + +[workspace] +name = "build_transitive" + +[[crates]] +name = "cc" +version = "0.1.0" +features = { parallel = [] } + +[[crates]] +name = "lib_sqlite" +version = "0.1.0" +build_dependencies = { cc = { version = "0.1.0" } } + +[[crates]] +name = "cmake_wrap" +version = "0.1.0" +dependencies = { cc = { version = "0.1.0", features = ["parallel"] } } + +[[crates]] +name = "lib_aws" +version = "0.1.0" +build_dependencies = { cmake_wrap = { version = "0.1.0" } } + +[[crates]] +name = "alpha" +version = "0.1.0" +workspace_member = true +dependencies = { lib_sqlite = { version = "0.1.0" } } + +[[crates]] +name = "beta" +version = "0.1.0" +workspace_member = true +dependencies = { lib_aws = { version = "0.1.0" } } diff --git a/workspace-gen/examples/derive_more_impl.toml b/workspace-gen/examples/derive_more_impl.toml new file mode 100644 index 0000000..7a04e54 --- /dev/null +++ b/workspace-gen/examples/derive_more_impl.toml @@ -0,0 +1,65 @@ +# Workspace: Proc-macro feature mismatch (derive_more-impl pattern) +# +# Tests: proc-macro crates (build-time deps) that are transitively reachable +# through normal dependency chains should be detected when features differ. +# +# This mirrors the real-world derive_more-impl case: +# daily (member) --normal--> inst --normal--> derive_more [no "is_variant"] +# engtool-reconstest-nse (member) --normal--> crossterm +# crossterm has: derive-more = ["dep:derive_more"] +# crossterm has: [dependencies] derive_more = { optional, features = ["is_variant"] } +# +# In this simplified version: +# alpha (member) --normal--> lib_a --normal--> derive_more [no "is_variant"] +# beta (member) --normal--> lib_b +# beta has intermediate feature: derive-more = ["dep:derive_more"] +# beta has optional dep on derive_more with features = ["is_variant"] +# beta's default feature enables the derive-more intermediate feature +# +# derive_more depends on derive_more_impl (proc-macro). +# derive_more's "is_variant" feature forwards to derive_more_impl/is_variant. +# The workspace resolves derive_more_impl with "is_variant" (from beta's chain). +# But alpha's chain only reaches derive_more_impl without "is_variant". +# +# The intermediate "derive-more = [dep:derive_more]" feature is critical: +# without it, the DFS goes directly from beta@derive_more to derive_more@default, +# skipping the is_variant edge. With it, the DFS must go through +# beta@derive-more -> beta@derive_more -> derive_more@is_variant. + +[workspace] +name = "derive_more_impl" + +[[crates]] +name = "derive_more_impl" +version = "0.1.0" +proc_macro = true +features = { is_variant = [] } + +[[crates]] +name = "derive_more" +version = "0.1.0" +dependencies = { derive_more_impl = { version = "0.1.0" } } +features = { is_variant = ["derive_more_impl/is_variant"] } + +[[crates]] +name = "lib_a" +version = "0.1.0" +dependencies = { derive_more = { version = "0.1.0" } } + +[[crates]] +name = "lib_b" +version = "0.1.0" +dependencies = {} + +[[crates]] +name = "beta" +version = "0.1.0" +workspace_member = true +dependencies = { lib_b = { version = "0.1.0" }, derive_more = { version = "0.1.0", optional = true, features = ["is_variant"] } } +features = { default = ["derive-more"], "derive-more" = ["dep:derive_more"] } + +[[crates]] +name = "alpha" +version = "0.1.0" +workspace_member = true +dependencies = { lib_a = { version = "0.1.0" } } diff --git a/workspace-gen/examples/devhmm.toml b/workspace-gen/examples/devhmm.toml new file mode 100644 index 0000000..53e93e2 --- /dev/null +++ b/workspace-gen/examples/devhmm.toml @@ -0,0 +1,49 @@ +# Workspace C: Host vs Target Dependency Isolation +# +# Tests: host (build) dependencies are NOT unified with target (normal) dependencies +# +# - alpha has normal dep on codec with features ["decode"] +# - alpha has build-dep on codec with features ["encode"] +# - beta has normal dep on codec with features ["encode", "optimize"] +# - beta has build-dep on codec with features ["decode"] +# - gamma has normal dep on codec and dev-dep on codec with different features +# +# Resolution: +# - Normal codec (target): features ["decode", "encode", "optimize"] unified +# - Build codec (host): features ["encode", "decode"] unified (separate from normal) +# - Dev codec (host): features ["test"] (separate from normal) +# +# proc-macro deps also run on host and don't unify with target deps + +[workspace] +name = "devhmm" + +[[crates]] +name = "codec" +features = { decode = [], encode = [], optimize = [], test = [], serialize = [] } + +[[crates]] +name = "pass1" +dependencies = { codec = { features = ["encode"] } } + +[[crates]] +name = "pass2" +features = { } +dependencies = { codec = { features = ["decode"] } } + + + + +[[crates]] +name = "alpha" +dependencies = { codec = { features = ["decode"] } } +dev_dependencies = { pass1 = {}} +build_dependencies = { pass1 = {} } +workspace_member = true + +[[crates]] +name = "beta" +dependencies = { codec = { features = ["optimize"] } } +build_dependencies = { pass2 = {}} +dev_dependencies = { pass2 = {}} +workspace_member = true diff --git a/workspace-gen/examples/missing_cc.toml b/workspace-gen/examples/missing_cc.toml new file mode 100644 index 0000000..6c113fd --- /dev/null +++ b/workspace-gen/examples/missing_cc.toml @@ -0,0 +1,49 @@ +# Workspace: Missing Feature on External Dependency (build deps) +# +# Tests: when two workspace members depend on the same external crate +# through build dependency chains, one with a feature and one without, +# the check command should detect the mismatch. +# +# Structure: +# cloud_utils (member) --BUILD--> cmake_wrap --normal--> cc (with "parallel") +# market_time (member) --BUILD--> build_helper --normal--> cc (no "parallel") +# +# The workspace resolves cc with "parallel" (from cloud_utils's chain). +# market_time should be flagged as missing the "parallel" feature on cc. +# +# Both members reach cc as Host instance through build dep chains, +# so the fix (Collect::NoDev for workspace collection) ensures +# cc_host with parallel is in the workspace feature set. + +[workspace] +name = "missing_cc" + +[[crates]] +name = "cc" +version = "0.1.0" +features = { parallel = [] } +workspace_member = false + +[[crates]] +name = "build_helper" +version = "0.1.0" +workspace_member = false +dependencies = { cc = { version = "0.1.0" } } + +[[crates]] +name = "cmake_wrap" +version = "0.1.0" +workspace_member = false +dependencies = { cc = { version = "0.1.0", features = ["parallel"] } } + +[[crates]] +name = "cloud_utils" +version = "0.1.0" +workspace_member = true +build_dependencies = { cmake_wrap = { version = "0.1.0" } } + +[[crates]] +name = "market_time" +version = "0.1.0" +workspace_member = true +build_dependencies = { build_helper = { version = "0.1.0" } } diff --git a/workspace-gen/examples/missing_cc2.toml b/workspace-gen/examples/missing_cc2.toml new file mode 100644 index 0000000..66a7f5e --- /dev/null +++ b/workspace-gen/examples/missing_cc2.toml @@ -0,0 +1,35 @@ +# Workspace: Missing External Dependency Entirely Absent from One Member +# +# Tests: when one workspace member depends on cc with "parallel" through +# a build dependency chain, while another member has no dependency on cc +# at all, the check command should detect that the second member is +# missing the cc dependency (with the "parallel" feature). +# +# Structure: +# cloud_utils (member) --BUILD--> cc (with "parallel") +# market_time (member) --no deps--> (cc absent from tree entirely) +# +# cc with "parallel" is in the workspace feature set (from cloud_utils). +# market_time should be flagged as needing to add cc with "parallel", +# but the check command currently fails to detect this because it only +# iterates over crates already present in a member's dependency tree. + +[workspace] +name = "missing_cc2" + +[[crates]] +name = "cc" +version = "0.1.0" +features = { parallel = [] } +workspace_member = false + +[[crates]] +name = "cloud_utils" +version = "0.1.0" +workspace_member = true +build_dependencies = { cc = { version = "0.1.0", features = ["parallel"] } } + +[[crates]] +name = "market_time" +version = "0.1.0" +workspace_member = true diff --git a/workspace-gen/examples/missing_cc_deep.toml b/workspace-gen/examples/missing_cc_deep.toml new file mode 100644 index 0000000..051edd7 --- /dev/null +++ b/workspace-gen/examples/missing_cc_deep.toml @@ -0,0 +1,48 @@ +# Workspace: Missing Feature - Deeper transitive chains +# +# Tests: same scenario but with more intermediate crate levels + +[workspace] +name = "missing_cc_deep" + +[[crates]] +name = "cc" +version = "0.1.0" +features = { parallel = [] } +workspace_member = false + +[[crates]] +name = "aws-lc-sys" +version = "0.1.0" +workspace_member = false +build_dependencies = { cc = { version = "0.1.0", features = ["parallel"] } } + +[[crates]] +name = "libsqlite3-sys" +version = "0.1.0" +workspace_member = false +build_dependencies = { cc = { version = "0.1.0" } } + +[[crates]] +name = "mid1" +version = "0.1.0" +workspace_member = false +dependencies = { aws-lc-sys = { version = "0.1.0" } } + +[[crates]] +name = "mid2" +version = "0.1.0" +workspace_member = false +dependencies = { libsqlite3-sys = { version = "0.1.0" } } + +[[crates]] +name = "cloud_utils" +version = "0.1.0" +workspace_member = true +dependencies = { mid1 = { version = "0.1.0" } } + +[[crates]] +name = "market_time" +version = "0.1.0" +workspace_member = true +dependencies = { mid2 = { version = "0.1.0" } } diff --git a/workspace-gen/examples/missing_cc_real.toml b/workspace-gen/examples/missing_cc_real.toml new file mode 100644 index 0000000..ea52d2a --- /dev/null +++ b/workspace-gen/examples/missing_cc_real.toml @@ -0,0 +1,46 @@ +# Workspace: Missing Feature on External Dependency (realistic scenario) +# +# Tests: when two workspace members have normal transitive dependencies on +# different crates that both have a build dependency on cc, one with the +# "parallel" feature and one without, the check command should detect +# the mismatch. +# +# Structure: +# cloud_utils (member) --normal--> aws-lc-sys --build--> cc (with "parallel") +# market_time (member) --normal--> libsqlite3-sys --build--> cc (no "parallel") +# +# This mirrors the actual bug report where check fails to detect the missing +# parallel feature on cc for market_time. + +[workspace] +name = "missing_cc_real" + +[[crates]] +name = "cc" +version = "0.1.0" +features = { parallel = [] } +workspace_member = false + +[[crates]] +name = "aws-lc-sys" +version = "0.1.0" +workspace_member = false +build_dependencies = { cc = { version = "0.1.0", features = ["parallel"] } } + +[[crates]] +name = "libsqlite3-sys" +version = "0.1.0" +workspace_member = false +build_dependencies = { cc = { version = "0.1.0" } } + +[[crates]] +name = "cloud_utils" +version = "0.1.0" +workspace_member = true +dependencies = { aws-lc-sys = { version = "0.1.0" } } + +[[crates]] +name = "market_time" +version = "0.1.0" +workspace_member = true +dependencies = { libsqlite3-sys = { version = "0.1.0" } } diff --git a/workspace-gen/examples/proc-macro.toml b/workspace-gen/examples/proc-macro.toml new file mode 100644 index 0000000..810c27d --- /dev/null +++ b/workspace-gen/examples/proc-macro.toml @@ -0,0 +1,47 @@ +# Workspace C: Host vs Target Dependency Isolation +# +# Tests: host (build) dependencies are NOT unified with target (normal) dependencies +# +# - alpha has normal dep on codec with features ["decode"] +# - alpha has build-dep on codec with features ["encode"] +# - beta has normal dep on codec with features ["encode", "optimize"] +# - beta has build-dep on codec with features ["decode"] +# - gamma has normal dep on codec and dev-dep on codec with different features +# +# Resolution: +# - Normal codec (target): features ["decode", "encode", "optimize"] unified +# - Build codec (host): features ["encode", "decode"] unified (separate from normal) +# - Dev codec (host): features ["test"] (separate from normal) +# +# proc-macro deps also run on host and don't unify with target deps + +[workspace] +name = "proc-macro" + +[[crates]] +name = "codec" +workspace_member = false +features = { decode = [], encode = [] } + +[[crates]] +name = "alpha" +workspace_member = true +dependencies = { macro-helper1 = [] } + +[[crates]] +name = "beta" +workspace_member = true +dependencies = { macro-helper2 = [] } + + +[[crates]] +name = "macro-helper1" +workspace_member = false +proc_macro = true +dependencies = { codec = { features = ["decode"] } } + +[[crates]] +name = "macro-helper2" +workspace_member = false +proc_macro = true +dependencies = { codec = { features = ["encode"] } } diff --git a/workspace-gen/examples/test_dev_dep.toml b/workspace-gen/examples/test_dev_dep.toml new file mode 100644 index 0000000..1b48ab8 --- /dev/null +++ b/workspace-gen/examples/test_dev_dep.toml @@ -0,0 +1,20 @@ +[workspace] +name = "test_dev_dep" + +[[crates]] +name = "cc" +version = "0.1.0" +features = { parallel = [] } +workspace_member = false + +[[crates]] +name = "cloud_utils" +version = "0.1.0" +workspace_member = true +build_dependencies = { cc = { version = "0.1.0", features = ["parallel"] } } + +[[crates]] +name = "market_time" +version = "0.1.0" +workspace_member = true +dev_dependencies = { cc = { version = "0.1.0" } } diff --git a/workspace-gen/examples/test_dev_transitive.toml b/workspace-gen/examples/test_dev_transitive.toml new file mode 100644 index 0000000..4b3e175 --- /dev/null +++ b/workspace-gen/examples/test_dev_transitive.toml @@ -0,0 +1,26 @@ +[workspace] +name = "test_dev_transitive" + +[[crates]] +name = "cc" +version = "0.1.0" +features = { parallel = [] } +workspace_member = false + +[[crates]] +name = "helper" +version = "0.1.0" +workspace_member = false +dependencies = { cc = { version = "0.1.0" } } + +[[crates]] +name = "cloud_utils" +version = "0.1.0" +workspace_member = true +build_dependencies = { cc = { version = "0.1.0", features = ["parallel"] } } + +[[crates]] +name = "market_time" +version = "0.1.0" +workspace_member = true +dev_dependencies = { helper = { version = "0.1.0" } } diff --git a/workspace-gen/examples/test_instance_mismatch.toml b/workspace-gen/examples/test_instance_mismatch.toml new file mode 100644 index 0000000..e8c9db4 --- /dev/null +++ b/workspace-gen/examples/test_instance_mismatch.toml @@ -0,0 +1,20 @@ +[workspace] +name = "test_instance_mismatch" + +[[crates]] +name = "cc" +version = "0.1.0" +features = { parallel = [] } +workspace_member = false + +[[crates]] +name = "cloud_utils" +version = "0.1.0" +workspace_member = true +build_dependencies = { cc = { version = "0.1.0", features = ["parallel"] } } + +[[crates]] +name = "market_time" +version = "0.1.0" +workspace_member = true +dependencies = { cc = { version = "0.1.0" } } diff --git a/workspace-gen/examples/test_optional_dep.toml b/workspace-gen/examples/test_optional_dep.toml new file mode 100644 index 0000000..4d449a2 --- /dev/null +++ b/workspace-gen/examples/test_optional_dep.toml @@ -0,0 +1,20 @@ +[workspace] +name = "test_optional_dep" + +[[crates]] +name = "cc" +version = "0.1.0" +features = { parallel = [] } +workspace_member = false + +[[crates]] +name = "cloud_utils" +version = "0.1.0" +workspace_member = true +build_dependencies = { cc = { version = "0.1.0", features = ["parallel"] } } + +[[crates]] +name = "market_time" +version = "0.1.0" +workspace_member = true +dependencies = { cc = { version = "0.1.0", optional = true } } diff --git a/workspace-gen/examples/unify-mio-remote.toml b/workspace-gen/examples/unify-mio-remote.toml new file mode 100644 index 0000000..c38b54a --- /dev/null +++ b/workspace-gen/examples/unify-mio-remote.toml @@ -0,0 +1,69 @@ +# Workspace: feature unification bug for `krate/feat` syntax on optional dep +# +# Reproduces a case where one workspace member (`dep_with`) enables a +# bunch of features on a third-party crate (`mio`) directly, while +# another member (`dep_without`) pulls in `mio` transitively through +# a crate (`tokio_like`) that has `mio` declared as an *optional* dep +# and uses the `mio/` syntax (Remote, not `dep:mio`) in its +# features. +# +# In the real-world case that motivated this test: +# - `tokio_like` declares `mio` as an *optional* dependency in +# both the unconditional `[dependencies]` table and a +# target-specific `[target.'cfg(...)'.dependencies]` table +# (mirroring how `tokio` declares `mio`) +# - `tokio_like`'s `io_uring` feature enables `mio/os_poll` and +# `mio/os_ext` via the `krate/feat` syntax +# - `dep_without` depends on `tokio_like` with `io_uring` enabled, +# so mio is pulled in with `os_poll` and `os_ext` +# - `dep_with` depends on `mio` directly with +# `default, log, net, os_ext, os_poll` +# - the workspace as a whole unifies `mio` with the full set +# +# Hackerman should detect that `dep_without` is missing the +# `default`, `log`, `net` mio features, but currently it does not +# because the `krate/feat` syntax on an optional dep is not turned +# into a `Trigger` (which is what wires up the soft dep correctly). +# +# The custom cfg `my_custom_flag` stands in for a real-world custom +# cfg like `tokio_unstable` that is not enabled by default in rustc +# and therefore does not appear in `rustc --print=cfg`. When the +# second mio dep's target cfg is not satisfied, the Remote edge from +# `io_uring` to `mio/os_poll` is filtered out, so mio never ends up +# in `dep_without`'s dep tree, and the check never gets a chance to +# compare mio's feature sets. + +[workspace] +name = "unify-mio-remote" + +[[crates]] +name = "log" +version = "0.1.0" + +[[crates]] +name = "mio" +version = "0.1.0" +dependencies = { log = { version = "0.1.0", optional = true } } +features = { log = ["dep:log"], os_poll = [], os_ext = ["os_poll"], net = [], default = ["log"] } + +[[crates]] +name = "tokio_like" +version = "0.1.0" +dependencies = { mio = { version = "0.1.0", optional = true, default_features = false } } +features = { io_uring = ["mio/os_poll", "mio/os_ext"] } + +[[crates.platform]] +target = 'cfg(my_custom_flag)' +dependencies = { mio = { version = "0.1.0", optional = true, default_features = false, features = ["os_poll", "os_ext"] } } + +[[crates]] +name = "dep_with" +version = "0.1.0" +workspace_member = true +dependencies = { mio = { version = "0.1.0", features = ["default", "log", "net", "os_ext", "os_poll"] } } + +[[crates]] +name = "dep_without" +version = "0.1.0" +workspace_member = true +dependencies = { tokio_like = { version = "0.1.0", features = ["io_uring"] } } diff --git a/workspace-gen/examples/unify-rand-chacha.toml b/workspace-gen/examples/unify-rand-chacha.toml new file mode 100644 index 0000000..c752d1e --- /dev/null +++ b/workspace-gen/examples/unify-rand-chacha.toml @@ -0,0 +1,43 @@ +# Workspace: hackerman suggests a `dep:` syntax as a feature +# +# Reproduces a case where hackerman reports a `dep:` syntax as if +# it were a real feature of the parent crate. +# +# In the real-world case that motivated this test: +# - `rand` declares `rand_chacha` as an *optional* dependency +# - `rand`'s `std_rng` feature enables it via `std_rng = ["dep:rand_chacha"]` +# - somewhere else in the workspace (e.g. `num_complex`) enables +# `rand/std_rng` as part of its own features +# - hackerman's check then suggests the dep crate (e.g. `cloud_utils`) +# add `rand = { features = ["default", "rand_chacha"] }`, but +# `rand_chacha` is not a feature of `rand` -- it is the name of the +# optional dependency +# +# The right suggestion is to add `rand = { features = ["std_rng"] }`, +# which is the real feature whose activation turns the optional dep on. + +[workspace] +name = "unify-rand-chacha" + +[[crates]] +name = "rand_chacha" +version = "0.1.0" +features = { std = [] } + +[[crates]] +name = "rand" +version = "0.1.0" +dependencies = { rand_chacha = { version = "0.1.0", optional = true, default_features = false } } +features = { std = ["rand_chacha?/std"], std_rng = ["dep:rand_chacha"], default = ["std"] } + +[[crates]] +name = "num_complex" +version = "0.1.0" +workspace_member = true +dependencies = { rand = { version = "0.1.0", features = ["std_rng"] } } + +[[crates]] +name = "cloud_utils" +version = "0.1.0" +workspace_member = true +dependencies = { rand = { version = "0.1.0" } } diff --git a/workspace-gen/examples/unify-serde-json-alloc.toml b/workspace-gen/examples/unify-serde-json-alloc.toml new file mode 100644 index 0000000..61dc85d --- /dev/null +++ b/workspace-gen/examples/unify-serde-json-alloc.toml @@ -0,0 +1,45 @@ +# Workspace: Feature unification issue not detected +# +# Reproduces a case where one workspace member (`livetrader`) enables a +# feature (`alloc`) on a third-party crate (`serde_json`) through a +# transitive optional dependency (`jsonptr`), while another member +# (`daily`) does not pull in that path. +# +# In the real-world case that motivated this test: +# - `jsonptr` declares `serde_json` as an *optional* dependency with +# `[dependencies.serde_json] features = ["alloc"]` +# - `jsonptr`'s `json` feature enables `dep:serde_json` +# - `livetrader` depends on `jsonptr` (with `json` enabled), so +# `serde_json` is pulled in with the `alloc` feature +# - `daily` does not depend on `jsonptr`, so when it is checked on its +# own `serde_json` has no `alloc` feature +# - the workspace as a whole unifies `serde_json` with `alloc` +# +# Hackerman should detect that `daily` is missing the `alloc` feature +# on `serde_json`, but currently it does not. + +[workspace] +name = "unify-serde-json-alloc" + +[[crates]] +name = "serde_json" +version = "0.1.0" +features = { alloc = [], std = [], default = ["std"] } + +[[crates]] +name = "jsonptr" +version = "0.1.0" +dependencies = { serde_json = { version = "0.1.0", optional = true, features = ["alloc"] } } +features = { json = ["dep:serde_json"], std = [], default = ["std", "json"] } + +[[crates]] +name = "livetrader" +version = "0.1.0" +workspace_member = true +dependencies = { jsonptr = { version = "0.1.0" } } + +[[crates]] +name = "daily" +version = "0.1.0" +workspace_member = true +dependencies = { serde_json = { version = "0.1.0" } } diff --git a/workspace-gen/examples/unify_basic.toml b/workspace-gen/examples/unify_basic.toml new file mode 100644 index 0000000..e658691 --- /dev/null +++ b/workspace-gen/examples/unify_basic.toml @@ -0,0 +1,26 @@ +# Workspace A: Basic Feature Unification +# +# Tests: regular dependencies are unified across workspace members +# +# When alpha depends on common with features ["a", "b"] +# and beta depends on common with features ["b", "c"], +# the unified resolution should enable features ["a", "b", "c"] on common. + +[workspace] +name = "unify_basic" + +[[crates]] +name = "common" +features = { a = [], b = [], c = [], d = [], default = ["b"] } + +[[crates]] +name = "alpha" +dependencies = { common = { features = ["a", "b"] } } + +[[crates]] +name = "beta" +dependencies = { common = { features = ["b", "c"] } } + +[[crates]] +name = "gamma" +dependencies = { common = { features = ["c", "d"], default-features = false } } diff --git a/workspace-gen/examples/unify_host_target.toml b/workspace-gen/examples/unify_host_target.toml new file mode 100644 index 0000000..f07e3d9 --- /dev/null +++ b/workspace-gen/examples/unify_host_target.toml @@ -0,0 +1,48 @@ +# Workspace C: Host vs Target Dependency Isolation +# +# Tests: host (build) dependencies are NOT unified with target (normal) dependencies +# +# - alpha has normal dep on codec with features ["decode"] +# - alpha has build-dep on codec with features ["encode"] +# - beta has normal dep on codec with features ["encode", "optimize"] +# - beta has build-dep on codec with features ["decode"] +# - gamma has normal dep on codec and dev-dep on codec with different features +# +# Resolution: +# - Normal codec (target): features ["decode", "encode", "optimize"] unified +# - Build codec (host): features ["encode", "decode"] unified (separate from normal) +# - Dev codec (host): features ["test"] (separate from normal) +# +# proc-macro deps also run on host and don't unify with target deps + +[workspace] +name = "unify_host_target" + +[[crates]] +name = "codec" +features = { decode = [], encode = [], optimize = [], test = [], serialize = [] } + +[[crates]] +name = "alpha" +dependencies = { codec = { features = ["decode"] } } +build_dependencies = { codec = { features = ["encode"] } } + +[[crates]] +name = "beta" +dependencies = { codec = { features = ["encode", "optimize"] } } +build_dependencies = { codec = { features = ["decode"] } } + +[[crates]] +name = "gamma" +dependencies = { codec = { features = ["decode"] } } +dev_dependencies = { codec = { features = ["test"] } } + +# proc-macro crate runs on host, its deps don't unify with target deps +[[crates]] +name = "macro-helper" +proc_macro = true +dependencies = { codec = { features = ["serialize"] } } + +[[crates]] +name = "app" +dependencies = { codec = { features = ["decode", "optimize"] }, macro-helper = {} } diff --git a/workspace-gen/examples/unify_platform.toml b/workspace-gen/examples/unify_platform.toml new file mode 100644 index 0000000..6c3e697 --- /dev/null +++ b/workspace-gen/examples/unify_platform.toml @@ -0,0 +1,42 @@ +# Workspace B: Platform-Specific Dependency Isolation +# +# Tests: platform specific dependencies are unified with each other, +# but NOT with regular target dependencies +# +# - alpha depends on widget with features ["base"] (regular) +# - alpha depends on widget with features ["android"] (platform-specific, cfg(target_os = "android")) +# - beta depends on widget with features ["base", "extra"] (regular) +# - beta depends on widget with features ["ios"] (platform-specific, cfg(target_os = "ios")) +# +# Resolution: +# - Regular widget: features ["base", "extra"] unified across alpha and beta +# - Android widget: features ["android"] (only from alpha) +# - iOS widget: features ["ios"] (only from beta) +# - Platform and regular do NOT unify with each other + +[workspace] +name = "unify_platform" + +[[crates]] +name = "widget" +features = { base = [], extra = [], android = [], ios = [], desktop = [] } + +[[crates]] +name = "alpha" +dependencies = { widget = { features = ["base"] } } + +[[crates.platform]] +target = 'cfg(target_os = "android")' +dependencies = { widget = { features = ["android"] } } + +[[crates]] +name = "beta" +dependencies = { widget = { features = ["base", "extra"] } } + +[[crates.platform]] +target = 'cfg(target_os = "ios")' +dependencies = { widget = { features = ["ios"] } } + +[[crates.platform]] +target = 'cfg(target_os = "android")' +dependencies = { widget = { features = ["extra"] } } diff --git a/workspace-gen/examples/weak-dep.toml b/workspace-gen/examples/weak-dep.toml new file mode 100644 index 0000000..bddef6d --- /dev/null +++ b/workspace-gen/examples/weak-dep.toml @@ -0,0 +1,73 @@ +# Workspace: Feature unification bug exposed by [patch] redirect +# +# Reproduces a case where hackerman fails to detect a unification issue +# because of how it matches resolved packages with their declared source +# in the metadata. When a workspace has a [patch] redirect, the dependency +# entry in the dependent crate still claims the original registry source, +# but the resolved package has a different source. Hackerman's +# `source_matches` check is too strict and silently drops the dependency, +# making the whole transitive path invisible to unification analysis. +# +# In the real-world case that motivated this test: +# - `models` depends on `quadprogpp` from crates.io +# - the workspace has a [patch.crates-io] redirecting `quadprogpp` to +# a git checkout that brings in `cxx` +# - `cxx` declares `std = ["alloc", "foldhash/std"]` +# - `hashbrown` (pulled in by `market_time`) enables `foldhash` as an +# optional dependency through `default-hasher`, without ever +# activating `foldhash/std` +# +# As a result: +# - `cargo check -p models` resolves foldhash with the `std` feature +# - `cargo check -p market_time` resolves foldhash with no extra +# features +# - the workspace as a whole unifies foldhash with `std` +# +# Hackerman should detect that `market_time` is missing the `std` feature +# on foldhash, but it does not because the `quadprogpp` dep is dropped +# during graph construction. + +[workspace] +name = "weak-dep" + +[patch.crates-io] +quadprogpp = { path = "quadprogpp-0.1.0" } + +[[crates]] +name = "foldhash" +version = "0.1.0" +features = { std = [] } + +[[crates]] +name = "cxx" +version = "0.1.0" +dependencies = { foldhash = { version = "0.1.0", default_features = false } } +features = { alloc = [], std = ["alloc", "foldhash/std"], default = ["std"] } + +[[crates]] +name = "quadprogpp_sys" +version = "0.1.0" +dependencies = { cxx = { version = "0.1.0" } } + +[[crates]] +name = "quadprogpp" +version = "0.1.0" +dependencies = { quadprogpp_sys = { version = "0.1.0" } } + +[[crates]] +name = "models" +version = "0.1.0" +workspace_member = true +dependencies = { quadprogpp = { version = "0.1.0", no_path = true } } + +[[crates]] +name = "hashbrown" +version = "0.1.0" +dependencies = { foldhash = { version = "0.1.0", optional = true, default_features = false } } +features = { default-hasher = ["dep:foldhash"], default = ["default-hasher"] } + +[[crates]] +name = "market_time" +version = "0.1.0" +workspace_member = true +dependencies = { hashbrown = { version = "0.1.0" } } diff --git a/workspace-gen/src/config.rs b/workspace-gen/src/config.rs new file mode 100644 index 0000000..b57fc11 --- /dev/null +++ b/workspace-gen/src/config.rs @@ -0,0 +1,182 @@ +use serde::Deserialize; +use serde::de::{self, Deserializer, MapAccess, SeqAccess, Visitor}; +use std::collections::BTreeMap; +use std::fmt; + +#[derive(Debug, Deserialize)] +pub struct WorkspaceConfig { + pub workspace: Workspace, + #[serde(default)] + pub crates: Vec, + /// Patches to apply to the workspace, e.g. `crates-io = { quadprogpp = { path = "..." } }` + #[serde(default)] + pub patch: BTreeMap>, +} + +/// A single patch entry. Mirrors `DependencySpec` for use under `[[patch]]`. +#[derive(Debug, Clone, Deserialize)] +#[serde(untagged)] +pub enum PatchDepSpec { + Path(String), + Config(DependencyConfig), +} + +impl WorkspaceConfig { + pub fn merge_duplicates(mut self) -> Self { + let mut merged: BTreeMap<(String, String), CrateConfig> = BTreeMap::new(); + + for crate_config in self.crates.drain(..) { + let key = (crate_config.name.clone(), crate_config.version.clone()); + match merged.get_mut(&key) { + Some(existing) => { + // Merge dependencies + for (k, v) in crate_config.dependencies { + existing.dependencies.entry(k).or_insert(v); + } + for (k, v) in crate_config.build_dependencies { + existing.build_dependencies.entry(k).or_insert(v); + } + for (k, v) in crate_config.dev_dependencies { + existing.dev_dependencies.entry(k).or_insert(v); + } + // Merge features (last wins for duplicates) + for (k, v) in crate_config.features { + existing.features.insert(k, v); + } + // Merge platform-specific deps + for platform in crate_config.platform { + existing.platform.push(platform); + } + } + None => { + merged.insert(key, crate_config); + } + } + } + + self.crates = merged.into_values().collect(); + self + } +} + +#[derive(Debug, Deserialize)] +pub struct Workspace { + pub name: String, +} + +#[derive(Debug, Deserialize)] +pub struct CrateConfig { + pub name: String, + #[serde(default = "default_version")] + pub version: String, + #[serde(default)] + pub workspace_member: bool, + #[serde(default)] + pub proc_macro: bool, + #[serde(default)] + pub dependencies: BTreeMap, + #[serde(default)] + pub build_dependencies: BTreeMap, + #[serde(default)] + pub dev_dependencies: BTreeMap, + #[serde(default)] + pub features: BTreeMap>, + #[serde(default)] + pub platform: Vec, +} + +fn default_version() -> String { + "0.1.0".to_string() +} + +#[derive(Debug, Clone)] +pub enum DependencySpec { + Config(DependencyConfig), + Features(Vec), +} + +impl DependencySpec { + pub fn to_config(&self) -> DependencyConfig { + match self { + DependencySpec::Config(cfg) => cfg.clone(), + DependencySpec::Features(features) => DependencyConfig { + version: None, + optional: None, + features: Some(features.clone()), + default_features: None, + path: None, + no_path: false, + }, + } + } +} + +impl<'de> Deserialize<'de> for DependencySpec { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct DependencySpecVisitor; + + impl<'de> Visitor<'de> for DependencySpecVisitor { + type Value = DependencySpec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string, a map, or a sequence") + } + + fn visit_str(self, s: &str) -> Result + where + E: de::Error, + { + Ok(DependencySpec::Config(DependencyConfig { + version: Some(s.to_string()), + ..Default::default() + })) + } + + fn visit_seq(self, seq: A) -> Result + where + A: SeqAccess<'de>, + { + let vec = Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))?; + Ok(DependencySpec::Features(vec)) + } + + fn visit_map(self, map: A) -> Result + where + A: MapAccess<'de>, + { + let cfg = Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?; + Ok(DependencySpec::Config(cfg)) + } + } + + deserializer.deserialize_any(DependencySpecVisitor) + } +} + +#[derive(Debug, Deserialize)] +pub struct PlatformConfig { + pub target: String, + #[serde(default)] + pub dependencies: BTreeMap, + #[serde(default)] + pub build_dependencies: BTreeMap, + #[serde(default)] + pub dev_dependencies: BTreeMap, +} + +#[derive(Debug, Clone, Deserialize, Default)] +pub struct DependencyConfig { + pub version: Option, + pub optional: Option, + pub features: Option>, + pub default_features: Option, + pub path: Option, + /// When set, the generator will not auto-inject a `path = "..."` for + /// this dependency. Useful for testing [patch] redirects and other + /// cases where the resolved source must differ from the declared one. + #[serde(default)] + pub no_path: bool, +} diff --git a/workspace-gen/src/fs.rs b/workspace-gen/src/fs.rs new file mode 100644 index 0000000..bf85484 --- /dev/null +++ b/workspace-gen/src/fs.rs @@ -0,0 +1,34 @@ +use std::fs; +use std::path::Path; + +use anyhow::Context; + +use crate::generator::GeneratedWorkspace; + +pub fn write_workspace(workspace: &GeneratedWorkspace, dry_run: bool) -> anyhow::Result<()> { + for (path, content) in &workspace.files { + if dry_run { + println!("Would create: {}", path.display()); + println!("---"); + println!("{}", content); + println!("---\n"); + } else { + let parent = path.parent().context("Failed to get parent directory")?; + fs::create_dir_all(parent) + .with_context(|| format!("Failed to create directory: {}", parent.display()))?; + fs::write(path, content) + .with_context(|| format!("Failed to write file: {}", path.display()))?; + println!("Created: {}", path.display()); + } + } + Ok(()) +} + +pub fn ensure_output_dir(path: &Path) -> anyhow::Result<()> { + if path.exists() { + // Remove existing workspace directory to start fresh + fs::remove_dir_all(path) + .with_context(|| format!("Failed to remove existing directory: {}", path.display()))?; + } + Ok(()) +} diff --git a/workspace-gen/src/generator.rs b/workspace-gen/src/generator.rs new file mode 100644 index 0000000..89dfab4 --- /dev/null +++ b/workspace-gen/src/generator.rs @@ -0,0 +1,254 @@ +use std::path::{Path, PathBuf}; + +use crate::config::{CrateConfig, DependencySpec, WorkspaceConfig}; + +pub struct GeneratedWorkspace { + pub root: PathBuf, + pub files: Vec<(PathBuf, String)>, +} + +pub fn generate(config: &WorkspaceConfig, output_base: &Path) -> GeneratedWorkspace { + let ws_name = &config.workspace.name; + let root = output_base.join(ws_name); + + let mut files = Vec::new(); + + // Generate workspace Cargo.toml + let members: Vec = config + .crates + .iter() + .filter(|c| c.workspace_member) + .map(|c| format!("\"{}-{}\"", c.name, c.version)) + .collect(); + + let excludes: Vec = config + .crates + .iter() + .filter(|c| !c.workspace_member) + .map(|c| format!("\"{}-{}\"", c.name, c.version)) + .collect(); + + let mut ws_toml = if excludes.is_empty() { + format!( + "[workspace]\nresolver = \"2\"\nmembers = [{}]\n", + members.join(", ") + ) + } else { + format!( + "[workspace]\nresolver = \"2\"\nmembers = [{}]\nexclude = [{}]\n", + members.join(", "), + excludes.join(", ") + ) + }; + + for (source, deps) in &config.patch { + ws_toml.push_str(&format!("[patch.{}]\n", source)); + for (dep_name, dep_spec) in deps { + ws_toml.push_str(&format!( + "{} = {}\n", + dep_name, + generate_patch_dep_line(dep_spec) + )); + } + } + + files.push((root.join("Cargo.toml"), ws_toml)); + + // Generate each crate + for crate_config in &config.crates { + let crate_dir = root.join(format!("{}-{}", crate_config.name, crate_config.version)); + + let cargo_toml = generate_crate_toml(crate_config); + files.push((crate_dir.join("Cargo.toml"), cargo_toml)); + + let lib_rs = if crate_config.proc_macro { + PROC_MACRO_LIB_RS.to_string() + } else { + LIB_RS.to_string() + }; + files.push((crate_dir.join("src/lib.rs"), lib_rs)); + } + + GeneratedWorkspace { root, files } +} + +fn generate_crate_toml(config: &CrateConfig) -> String { + let mut out = String::new(); + + out.push_str("[package]\n"); + out.push_str(&format!("name = \"{}\"\n", config.name)); + out.push_str(&format!("version = \"{}\"\n", config.version)); + out.push_str("edition = \"2024\"\n\n"); + + // Proc-macro crate type + if config.proc_macro { + out.push_str("[lib]\n"); + out.push_str("proc-macro = true\n\n"); + } + + // Dependencies + if !config.dependencies.is_empty() { + out.push_str("[dependencies]\n"); + for (dep_name, dep_config) in &config.dependencies { + out.push_str(&generate_dep_line(dep_name, dep_config)); + } + out.push('\n'); + } + + // Build dependencies + if !config.build_dependencies.is_empty() { + out.push_str("[build-dependencies]\n"); + for (dep_name, dep_config) in &config.build_dependencies { + out.push_str(&generate_dep_line(dep_name, dep_config)); + } + out.push('\n'); + } + + // Dev dependencies + if !config.dev_dependencies.is_empty() { + out.push_str("[dev-dependencies]\n"); + for (dep_name, dep_config) in &config.dev_dependencies { + out.push_str(&generate_dep_line(dep_name, dep_config)); + } + out.push('\n'); + } + + // Platform-specific dependencies + for platform in &config.platform { + if !platform.dependencies.is_empty() { + out.push_str(&format!("[target.'{}'.dependencies]\n", platform.target)); + for (dep_name, dep_config) in &platform.dependencies { + out.push_str(&generate_dep_line(dep_name, dep_config)); + } + out.push('\n'); + } + + if !platform.build_dependencies.is_empty() { + out.push_str(&format!( + "[target.'{}'.build-dependencies]\n", + platform.target + )); + for (dep_name, dep_config) in &platform.build_dependencies { + out.push_str(&generate_dep_line(dep_name, dep_config)); + } + out.push('\n'); + } + + if !platform.dev_dependencies.is_empty() { + out.push_str(&format!( + "[target.'{}'.dev-dependencies]\n", + platform.target + )); + for (dep_name, dep_config) in &platform.dev_dependencies { + out.push_str(&generate_dep_line(dep_name, dep_config)); + } + out.push('\n'); + } + } + + // Features + if !config.features.is_empty() { + out.push_str("[features]\n"); + for (feat_name, feat_deps) in &config.features { + if feat_deps.is_empty() { + out.push_str(&format!("{} = []\n", feat_name)); + } else { + out.push_str(&format!( + "{} = [{}]\n", + feat_name, + format_deps_list(feat_deps) + )); + } + } + } + + out +} + +fn generate_dep_line(dep_name: &str, spec: &DependencySpec) -> String { + let config = spec.to_config(); + + // If `no_path` is set and there is no path, render the dep as a + // bare version spec (e.g. `quadprogpp = "0.1.0"`). This is useful + // for testing [patch] redirects and similar. + if config.no_path + && config.path.is_none() + && let Some(version) = &config.version + { + let mut parts = Vec::new(); + parts.push(format!("version = \"{}\"", version)); + if let Some(true) = config.optional { + parts.push("optional = true".to_string()); + } + if let Some(false) = config.default_features { + parts.push("default-features = false".to_string()); + } + if let Some(ref features) = config.features { + let feat_str: Vec = features.iter().map(|f| format!("\"{}\"", f)).collect(); + parts.push(format!("features = [{}]", feat_str.join(", "))); + } + if parts.len() == 1 { + return format!("{} = \"{}\"\n", dep_name, version); + } + return format!("{} = {{ {} }}\n", dep_name, parts.join(", ")); + } + + let mut parts = Vec::new(); + + // Determine path - skip if `no_path` is set (e.g. for [patch] test cases) + if !config.no_path { + let path = if let Some(ref p) = config.path { + p.clone() + } else { + let dep_version = config.version.as_deref().unwrap_or("0.1.0"); + format!("../{}-{}", dep_name, dep_version) + }; + parts.push(format!("path = \"{}\"", path)); + } + + if let Some(optional) = config.optional + && optional + { + parts.push("optional = true".to_string()); + } + + if let Some(default_features) = config.default_features + && !default_features + { + parts.push("default-features = false".to_string()); + } + + if let Some(ref features) = config.features { + let feat_str: Vec = features.iter().map(|f| format!("\"{}\"", f)).collect(); + parts.push(format!("features = [{}]", feat_str.join(", "))); + } + + format!("{} = {{ {} }}\n", dep_name, parts.join(", ")) +} + +fn format_deps_list(deps: &[String]) -> String { + deps.iter() + .map(|d| format!("\"{}\"", d)) + .collect::>() + .join(", ") +} + +fn generate_patch_dep_line(spec: &crate::config::PatchDepSpec) -> String { + match spec { + crate::config::PatchDepSpec::Path(p) => format!("{{ path = \"{}\" }}", p), + crate::config::PatchDepSpec::Config(c) => { + let mut parts = Vec::new(); + if let Some(ref p) = c.path { + parts.push(format!("path = \"{}\"", p)); + } + if let Some(true) = c.optional { + parts.push("optional = true".to_string()); + } + format!("{{ {} }}", parts.join(", ")) + } + } +} + +const LIB_RS: &str = "#[cfg(test)] mod tests { #[test] fn it_works() { assert!(true); } }"; + +const PROC_MACRO_LIB_RS: &str = "extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro] pub fn identity(input: TokenStream) -> TokenStream { input } "; diff --git a/workspace-gen/src/lib.rs b/workspace-gen/src/lib.rs new file mode 100644 index 0000000..5f69433 --- /dev/null +++ b/workspace-gen/src/lib.rs @@ -0,0 +1,3 @@ +pub mod config; +pub mod fs; +pub mod generator; diff --git a/workspace-gen/src/main.rs b/workspace-gen/src/main.rs new file mode 100644 index 0000000..112c196 --- /dev/null +++ b/workspace-gen/src/main.rs @@ -0,0 +1,51 @@ +use std::fs; +use std::path::PathBuf; + +use anyhow::{Context, Result}; +use bpaf::Bpaf; + +use workspace_gen::config::WorkspaceConfig; +use workspace_gen::fs::{ensure_output_dir, write_workspace}; +use workspace_gen::generator; + +#[derive(Bpaf)] +#[bpaf(options)] +struct Cli { + /// Output directory (default: test_workspaces) + #[bpaf(short, long, argument("DIR"), fallback("test_workspaces".into()))] + output: PathBuf, + + /// Dry run - show what would be generated without writing files + #[bpaf(short, long)] + dry_run: bool, + + /// Path to the TOML configuration file + #[bpaf(positional("CONFIG"))] + config: PathBuf, +} + +fn main() -> Result<()> { + let cli = cli().fallback_to_usage().run(); + + let content = fs::read_to_string(&cli.config) + .with_context(|| format!("Failed to read config file: {}", cli.config.display()))?; + + let config: WorkspaceConfig = toml::from_str::(&content) + .with_context(|| format!("Failed to parse config file: {}", cli.config.display()))? + .merge_duplicates(); + + let workspace = generator::generate(&config, &cli.output); + + if !cli.dry_run { + let ws_dir = cli.output.join(&config.workspace.name); + ensure_output_dir(&ws_dir)?; + } + + write_workspace(&workspace, cli.dry_run)?; + + if !cli.dry_run { + println!("\nWorkspace generated at: {}", workspace.root.display()); + } + + Ok(()) +}