From 582c3f0fa2b41c9e4e30deffd79a8c5a3294c738 Mon Sep 17 00:00:00 2001 From: RanaPriyansh <87440899+RanaPriyansh@users.noreply.github.com> Date: Fri, 18 Sep 2026 22:24:19 +0530 Subject: [PATCH] add registry-backed WIT resolution --- Cargo.lock | 1201 ++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 1 + README.md | 13 + src/command.rs | 32 +- src/lib.rs | 78 ++- src/python.rs | 52 +- src/registry.rs | 209 +++++++ src/test.rs | 2 + src/test/tests.rs | 2 + tests/bindings.rs | 442 +++++++++++++++ tests/componentize.rs | 116 ++++ 11 files changed, 2041 insertions(+), 107 deletions(-) create mode 100644 src/registry.rs diff --git a/Cargo.lock b/Cargo.lock index c8842db8..2cd76cbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,6 +53,15 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "1.0.0" @@ -144,13 +153,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] @@ -171,18 +180,66 @@ dependencies = [ "topological-sort", ] +[[package]] +name = "auditable-serde" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d026218ae25ba5c72834245412dd1338f6d270d2c5109ee03a4badec288d4056" +dependencies = [ + "semver", + "serde", + "serde_json", + "topological-sort", +] + [[package]] name = "autocfg" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "aws-lc-rs" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" +dependencies = [ + "aws-lc-sys", + "untrusted 0.7.1", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "bincode" version = "1.3.3" @@ -231,6 +288,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + [[package]] name = "brotli" version = "8.0.3" @@ -274,9 +340,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cap-fs-ext" @@ -385,6 +451,20 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + [[package]] name = "clap" version = "4.6.1" @@ -416,7 +496,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -425,6 +505,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + [[package]] name = "cobs" version = "0.3.0" @@ -440,6 +529,16 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "combine" +version = "4.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "component-init-transform" version = "0.2.0" @@ -479,7 +578,7 @@ dependencies = [ "pretty_env_logger", "proptest", "pyo3", - "reqwest", + "reqwest 0.12.28", "semver", "serde", "tar", @@ -487,6 +586,7 @@ dependencies = [ "tokio", "toml 0.8.23", "wasm-encoder 0.258.0", + "wasm-pkg-client", "wasmparser 0.258.0", "wasmtime", "wasmtime-wasi", @@ -539,6 +639,49 @@ version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "const_format" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpp_demangle" version = "0.5.1" @@ -630,7 +773,7 @@ dependencies = [ "rustc-hash", "serde", "serde_derive", - "sha2", + "sha2 0.10.9", "smallvec", "target-lexicon", "wasmtime-internal-core", @@ -756,6 +899,50 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.117", +] + [[package]] name = "debugid" version = "0.8.0" @@ -765,6 +952,37 @@ dependencies = [ "uuid", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.117", +] + [[package]] name = "difflib" version = "0.4.0" @@ -777,8 +995,19 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid", + "crypto-common 0.2.2", ] [[package]] @@ -810,9 +1039,26 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] +[[package]] +name = "docker_credential" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29547a1dc60885a552306986316bc9701ba120c1a8db6769fa68691529ad373d" +dependencies = [ + "base64 0.22.1", + "serde", + "serde_json", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "either" version = "1.16.0" @@ -869,6 +1115,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "etcetera" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" +dependencies = [ + "cfg-if", + "windows-sys 0.61.2", +] + [[package]] name = "fastrand" version = "2.4.1" @@ -897,6 +1153,12 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.9" @@ -977,9 +1239,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -987,9 +1249,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" @@ -1004,38 +1266,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -1113,6 +1375,17 @@ dependencies = [ "wasip3", ] +[[package]] +name = "getset" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cf442baaabe4213ce7d1239afc26c039180b6456da2cededa316ae2c8a77a77" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "gimli" version = "0.33.0" @@ -1125,6 +1398,25 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "h2" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -1180,6 +1472,15 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-auth" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "150fa4a9462ef926824cf4519c84ed652ca8f4fbae34cb8af045b5cbcaf98822" +dependencies = [ + "memchr", +] + [[package]] name = "http-body" version = "1.0.1" @@ -1215,6 +1516,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.10.1" @@ -1225,6 +1535,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", + "h2", "http", "http-body", "httparse", @@ -1257,7 +1568,7 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-util", @@ -1274,6 +1585,30 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "2.2.0" @@ -1362,6 +1697,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.1.0" @@ -1499,6 +1840,55 @@ dependencies = [ "cc", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "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 2.0.117", +] + +[[package]] +name = "jni-sys" +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 = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -1520,6 +1910,43 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "10.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc" +dependencies = [ + "aws-lc-rs", + "base64 0.22.1", + "getrandom 0.2.17", + "js-sys", + "serde", + "serde_json", + "signature", + "zeroize", +] + +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "leb128" version = "0.2.6" @@ -1583,6 +2010,15 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b" +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + [[package]] name = "maybe-owned" version = "0.3.4" @@ -1604,6 +2040,12 @@ dependencies = [ "rustix", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1671,6 +2113,78 @@ dependencies = [ "memchr", ] +[[package]] +name = "oci-client" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5261a7fb43d9c53b8e63e6d5e86860719dad253d015d022066c72d585125aed8" +dependencies = [ + "bytes", + "chrono", + "futures-util", + "hex", + "http", + "http-auth", + "jsonwebtoken", + "lazy_static", + "oci-spec", + "olpc-cjson", + "regex", + "reqwest 0.13.5", + "serde", + "serde_json", + "sha2 0.11.0", + "thiserror 2.0.18", + "tokio", + "tracing", + "unicase", +] + +[[package]] +name = "oci-spec" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8445a2631507cec628a15fdd6154b54a3ab3f20ed4fe9d73a3b8b7a4e1ba03a" +dependencies = [ + "const_format", + "derive_builder", + "getset", + "regex", + "serde", + "serde_json", + "strum", + "strum_macros", + "thiserror 2.0.18", +] + +[[package]] +name = "oci-wasm" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87689298bd74f0f2675fcac99956a34c31098ca3bdced3d7635e71dc03c5ca21" +dependencies = [ + "anyhow", + "chrono", + "oci-client", + "serde", + "serde_json", + "sha2 0.11.0", + "tokio", + "wit-component 0.253.0", + "wit-parser 0.253.0", +] + +[[package]] +name = "olpc-cjson" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "696183c9b5fe81a7715d074fd632e8bd46f4ccc0231a3ed7fc580a80de5f7083" +dependencies = [ + "serde", + "serde_json", + "unicode-normalization", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -1683,6 +2197,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "percent-encoding" version = "2.3.2" @@ -1695,8 +2215,20 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ - "fixedbitset", + "fixedbitset 0.4.2", + "indexmap", +] + +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset 0.5.7", + "hashbrown 0.15.5", "indexmap", + "serde", ] [[package]] @@ -1794,7 +2326,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.117", ] [[package]] @@ -1845,7 +2377,7 @@ checksum = "92e6c1756d5b8ce2a6353593fc15a21ffbfbc7c030f0ea02eb53d61aa4121c07" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1892,7 +2424,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1905,7 +2437,7 @@ dependencies = [ "proc-macro2", "pyo3-build-config", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1940,6 +2472,7 @@ version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ + "aws-lc-rs", "bytes", "getrandom 0.3.4", "lru-slab", @@ -2026,6 +2559,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] [[package]] name = "rand_core" @@ -2141,7 +2677,7 @@ version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-core", @@ -2175,6 +2711,50 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "reqwest" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029" +dependencies = [ + "base64 0.23.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + [[package]] name = "ring" version = "0.17.14" @@ -2185,7 +2765,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.17", "libc", - "untrusted", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -2201,6 +2781,15 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "1.1.4" @@ -2230,6 +2819,7 @@ version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ + "aws-lc-rs", "once_cell", "ring", "rustls-pki-types", @@ -2238,6 +2828,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" version = "1.14.1" @@ -2248,15 +2850,43 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -2283,6 +2913,57 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "serde", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.28" @@ -2320,7 +3001,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2374,7 +3055,27 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", ] [[package]] @@ -2383,12 +3084,37 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "simd-adler32" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +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 = "sized-chunks" version = "0.6.5" @@ -2433,6 +3159,15 @@ dependencies = [ "smallvec", ] +[[package]] +name = "spdx" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081670c233dfbed55690cc0cd38424e0e24ac1b2673d0b408b3f7b684738dfa9" +dependencies = [ + "smallvec", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -2445,6 +3180,24 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "subtle" version = "2.6.1" @@ -2462,6 +3215,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -2479,7 +3243,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2553,7 +3317,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2564,7 +3328,16 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "thread_local" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" +dependencies = [ + "cfg-if", ] [[package]] @@ -2594,9 +3367,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", @@ -2615,7 +3388,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2630,13 +3403,14 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ "bytes", "futures-core", "futures-sink", + "libc", "pin-project-lite", "tokio", ] @@ -2668,6 +3442,21 @@ dependencies = [ "winnow 0.7.15", ] +[[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 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.3", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -2686,6 +3475,15 @@ dependencies = [ "serde_core", ] +[[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_edit" version = "0.22.27" @@ -2783,6 +3581,7 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2796,7 +3595,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2808,6 +3607,21 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "once_cell", + "regex-automata", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -2826,12 +3640,27 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.2.2" @@ -2844,6 +3673,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -2899,6 +3734,16 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2974,7 +3819,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -2997,7 +3842,7 @@ dependencies = [ "heck", "indexmap", "log", - "petgraph", + "petgraph 0.6.5", "smallvec", "wasm-encoder 0.254.0", "wasmparser 0.254.0", @@ -3014,6 +3859,16 @@ dependencies = [ "wasmparser 0.244.0", ] +[[package]] +name = "wasm-encoder" +version = "0.253.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59972d6cd272259de647b7c1f1912e45e289c75ffd4be04e10695507cd7e1b59" +dependencies = [ + "leb128fmt", + "wasmparser 0.253.0", +] + [[package]] name = "wasm-encoder" version = "0.254.0" @@ -3041,18 +3896,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "auditable-serde", + "auditable-serde 0.8.0", "flate2", "indexmap", "serde", "serde_derive", "serde_json", - "spdx", + "spdx 0.10.9", "url", "wasm-encoder 0.244.0", "wasmparser 0.244.0", ] +[[package]] +name = "wasm-metadata" +version = "0.253.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3f45816ef616806f48498bcd831377de578c4fa51db0c83ab8ceb78cc13523b" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder 0.253.0", + "wasmparser 0.253.0", +] + [[package]] name = "wasm-metadata" version = "0.254.0" @@ -3060,7 +3927,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b01df5f3b4ca7881e843f3bc0fb8a3905d79c68692250dcb8e33e698705ccdb6" dependencies = [ "anyhow", + "auditable-serde 0.9.0", + "flate2", "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx 0.13.5", + "url", "wasm-encoder 0.254.0", "wasmparser 0.254.0", ] @@ -3077,6 +3951,76 @@ dependencies = [ "wasmparser 0.258.0", ] +[[package]] +name = "wasm-pkg-client" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39aa5033e60ecdd6c7015ef6450f3e3141c3a04c8bbeedbedb49119c7fc82d4" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.23.1", + "bytes", + "docker_credential", + "etcetera", + "futures-util", + "oci-client", + "oci-wasm", + "petgraph 0.8.3", + "reqwest 0.13.5", + "secrecy", + "serde", + "serde_json", + "sha2 0.11.0", + "tempfile", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "toml 1.1.2+spec-1.1.0", + "tracing", + "tracing-subscriber", + "url", + "wasm-metadata 0.254.0", + "wasm-pkg-common", + "wit-component 0.254.0", + "wit-parser 0.254.0", +] + +[[package]] +name = "wasm-pkg-common" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2c316a266be61c22568d7c3534046964535b44317c2f5ee8b5c0b8e0001b7e6" +dependencies = [ + "anyhow", + "base16ct", + "bytes", + "etcetera", + "futures-util", + "http", + "semver", + "serde", + "serde_json", + "sha2 0.11.0", + "thiserror 2.0.18", + "tokio", + "toml 1.1.2+spec-1.1.0", + "tracing", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmparser" version = "0.244.0" @@ -3090,6 +4034,18 @@ dependencies = [ "serde", ] +[[package]] +name = "wasmparser" +version = "0.253.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19db11f87d2486580e1e8b6f494c54df7e0566b87d0b599db843c24019667339" +dependencies = [ + "bitflags", + "hashbrown 0.17.1", + "indexmap", + "semver", +] + [[package]] name = "wasmparser" version = "0.254.0" @@ -3201,7 +4157,7 @@ dependencies = [ "semver", "serde", "serde_derive", - "sha2", + "sha2 0.10.9", "smallvec", "target-lexicon", "wasm-encoder 0.254.0", @@ -3217,14 +4173,14 @@ version = "48.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e1464c6a7ece16f1aeeab9a62d4a821581d143d6dfd462a99b66b04d31431da" dependencies = [ - "base64", + "base64 0.22.1", "directories-next", "log", "postcard", "rustix", "serde", "serde_derive", - "sha2", + "sha2 0.10.9", "toml 0.9.12+spec-1.1.0", "wasmtime-environ", "windows-sys 0.61.2", @@ -3240,7 +4196,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasmtime-internal-component-util", "wasmtime-internal-wit-bindgen", "wit-parser 0.254.0", @@ -3347,7 +4303,7 @@ checksum = "fb0941017d1ae98c02bc7ab1935d6ccb037481fe6d3dad49cb43de19781ffa42" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3452,6 +4408,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" version = "1.0.7" @@ -3484,7 +4449,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasmtime-environ", "witx", ] @@ -3497,7 +4462,7 @@ checksum = "c31d70e43e2cc7c981d77b52240c880f9a00d5a33f94c5bf878827d92f2be862" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wiggle-generate", ] @@ -3532,12 +4497,65 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -3784,7 +4802,7 @@ dependencies = [ "heck", "indexmap", "prettyplease", - "syn", + "syn 2.0.117", "wasm-metadata 0.244.0", "wit-bindgen-core 0.51.0", "wit-component 0.244.0", @@ -3800,7 +4818,7 @@ dependencies = [ "heck", "indexmap", "prettyplease", - "syn", + "syn 2.0.117", "wasm-metadata 0.258.0", "wit-bindgen-core 0.61.0", "wit-component 0.258.0", @@ -3816,7 +4834,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wit-bindgen-core 0.51.0", "wit-bindgen-rust 0.51.0", ] @@ -3831,7 +4849,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wit-bindgen-core 0.61.0", "wit-bindgen-rust 0.61.0", ] @@ -3855,6 +4873,25 @@ dependencies = [ "wit-parser 0.244.0", ] +[[package]] +name = "wit-component" +version = "0.253.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbbd2500ac3488489ee8c6e59b79d7e47e6da5bfb019efd35d5dca57b78af624" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.253.0", + "wasm-metadata 0.253.0", + "wasmparser 0.253.0", + "wit-parser 0.253.0", +] + [[package]] name = "wit-component" version = "0.254.0" @@ -3871,6 +4908,7 @@ dependencies = [ "wasm-encoder 0.254.0", "wasm-metadata 0.254.0", "wasmparser 0.254.0", + "wat", "wit-parser 0.254.0", ] @@ -3928,6 +4966,25 @@ dependencies = [ "wasmparser 0.244.0", ] +[[package]] +name = "wit-parser" +version = "0.253.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d997b8e5920fcbeec742b58e583325d6419a6aca617ae8075c406a61c65ba8a" +dependencies = [ + "anyhow", + "hashbrown 0.17.1", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-ident", + "wasmparser 0.253.0", +] + [[package]] name = "wit-parser" version = "0.254.0" @@ -4013,7 +5070,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -4034,7 +5091,7 @@ checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4054,7 +5111,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -4063,6 +5120,20 @@ name = "zeroize" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] [[package]] name = "zerotrie" @@ -4094,7 +5165,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7804fb50..8c830968 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ pyo3 = { version = "0.28.3", features = [ ], optional = true } wasmtime = "48.0.0" wasmtime-wasi = { version = "48.0.0", features = [ "p3" ] } +wasm-pkg-client = "0.16.1" once_cell = "1.20.2" component-init-transform = "0.2.0" async-trait = "0.1.83" diff --git a/README.md b/README.md index 6f4e4a61..f1a57d5c 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,19 @@ And finally generate the component: componentize-py -d hello.wit -w hello componentize --stub-wasi app -o app.wasm ``` +An exact, versioned world can also be loaded from a WebAssembly package +registry: + +```shell +componentize-py -w wasi:cli/command@0.2.0 bindings wasi_cli +``` + +Use `--registry-config PATH` to merge a standard `wasm-pkg` configuration file +with the global configuration. Use `--no-default-registries` to start with an +empty configuration before loading that file. Registry loading is lazy, and a +matching local WIT definition takes precedence. Remote world references must +use the exact `namespace:package/world@version` form. + To test it, you can install `wasmtime-py` and write a simple host app which uses it to load and run our component: diff --git a/src/command.rs b/src/command.rs index 8e5438dc..0d9e03c7 100644 --- a/src/command.rs +++ b/src/command.rs @@ -104,6 +104,14 @@ pub struct Common { /// If this is not specified, the module name will default to "wit". #[arg(long)] pub bindings_module: Option, + + /// Path to a wasm package registry configuration file. + #[arg(long)] + pub registry_config: Option, + + /// Disable the global and built-in registry mappings. + #[arg(long)] + pub no_default_registries: bool, } #[derive(clap::Subcommand, Debug)] @@ -242,7 +250,7 @@ pub fn run + Clone, I: IntoIterator>(args: I) -> Res } fn generate_bindings(common: Common, bindings: Bindings) -> Result<()> { - BindingsGenerator { + let generator = BindingsGenerator { wit_paths: &common .wit_path .iter() @@ -268,8 +276,10 @@ fn generate_bindings(common: Common, bindings: Bindings) -> Result<()> { .iter() .map(|(a, b)| (a.as_str(), b.as_str())) .collect(), - } - .generate() + registry_config: common.registry_config.as_deref(), + default_registries: !common.no_default_registries, + }; + Runtime::new()?.block_on(generator.generate()) } fn componentize(common: Common, componentize: Componentize) -> Result<()> { @@ -323,6 +333,8 @@ fn componentize(common: Common, componentize: Componentize) -> Result<()> { .map(|(a, b)| (a.as_str(), b.as_str())) .collect(), intersect_world: componentize.intersect_world.as_deref(), + registry_config: common.registry_config.as_deref(), + default_registries: !common.no_default_registries, } .generate(), )?; @@ -461,6 +473,8 @@ mod tests { all_features: false, import_interface_name: Vec::new(), export_interface_name: Vec::new(), + registry_config: None, + no_default_registries: false, }; let bindings = bindings(out_dir.path()); generate_bindings(common, bindings)?; @@ -489,6 +503,8 @@ mod tests { all_features: false, import_interface_name: Vec::new(), export_interface_name: Vec::new(), + registry_config: None, + no_default_registries: false, }; let bindings = bindings(out_dir.path()); generate_bindings(common, bindings)?; @@ -517,6 +533,8 @@ mod tests { all_features: true, import_interface_name: Vec::new(), export_interface_name: Vec::new(), + registry_config: None, + no_default_registries: false, }; let bindings = bindings(out_dir.path()); generate_bindings(common, bindings)?; @@ -544,6 +562,8 @@ mod tests { all_features: false, import_interface_name: Vec::new(), export_interface_name: Vec::new(), + registry_config: None, + no_default_registries: false, }; let bindings = bindings(out_dir.path()); generate_bindings(common.clone(), bindings)?; @@ -654,6 +674,8 @@ world lib-world { all_features: false, import_interface_name: Vec::new(), export_interface_name: Vec::new(), + registry_config: None, + no_default_registries: false, }, bindings(lib_wit_dir), )?; @@ -668,6 +690,8 @@ world lib-world { all_features: false, import_interface_name: Vec::new(), export_interface_name: Vec::new(), + registry_config: None, + no_default_registries: false, }, Componentize { app_name: "app".into(), @@ -702,6 +726,8 @@ world lib-world { all_features: false, import_interface_name: Vec::new(), export_interface_name: Vec::new(), + registry_config: None, + no_default_registries: false, } } diff --git a/src/lib.rs b/src/lib.rs index a3b7dae4..dd3fe44c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,7 @@ mod link; mod prelink; #[cfg(feature = "pyo3")] mod python; +mod registry; mod stubwasi; mod summary; #[cfg(test)] @@ -183,12 +184,14 @@ pub struct BindingsGenerator<'a> { pub output_dir: &'a Path, pub import_interface_names: &'a HashMap<&'a str, &'a str>, pub export_interface_names: &'a HashMap<&'a str, &'a str>, + pub registry_config: Option<&'a Path>, + pub default_registries: bool, /// Write into `output_dir` even if the bindings module directory exists. pub allow_existing: bool, } impl BindingsGenerator<'_> { - pub fn generate(&self) -> Result<()> { + pub async fn generate(&self) -> Result<()> { // Here we parse the specified WIT paths and resolve the specified // worlds, then union them together and generate stub code for the // unioned world. @@ -215,12 +218,27 @@ impl BindingsGenerator<'_> { packages.push((path, resolve.push_path(path)?.0)); } - if packages.is_empty() { + if packages.is_empty() + && (Path::new("wit").exists() + || !self + .worlds + .iter() + .any(|world| registry::is_qualified_world_reference(world))) + { // If no WIT directory was provided as a parameter, use ./wit by default. packages.push((Path::new("wit"), resolve.push_path("wit")?.0)); } - let worlds = select_worlds(&resolve, self.worlds, &packages)?; + let worlds = registry::resolve_requested_worlds( + &mut resolve, + &packages, + self.worlds, + self.registry_config, + self.default_registries, + ) + .await? + .into_iter() + .collect::>(); let world = match &worlds.iter().copied().collect::>()[..] { [] => select_world(&resolve, None, &packages)?, &[world] => world, @@ -395,6 +413,8 @@ pub struct ComponentGenerator<'a> { pub import_interface_names: &'a HashMap<&'a str, &'a str>, pub export_interface_names: &'a HashMap<&'a str, &'a str>, pub intersect_world: Option<&'a str>, + pub registry_config: Option<&'a Path>, + pub default_registries: bool, } impl ComponentGenerator<'_> { @@ -550,16 +570,47 @@ impl ComponentGenerator<'_> { packages.push((path, resolve.push_path(path)?.0)); } - if packages.is_empty() { + let registry_worlds = self + .worlds + .iter() + .copied() + .chain(self.intersect_world) + .collect::>(); + + if packages.is_empty() + && (Path::new("wit").exists() + || !registry_worlds + .iter() + .any(|world| registry::is_qualified_world_reference(world))) + { // If no WIT directory was provided as a parameter and none were // referenced by Python packages, use ./wit by default. packages.push((Path::new("wit"), resolve.push_path("wit")?.0)); } - let worlds = select_worlds(&resolve, self.worlds, &packages)?; + let default_world = + if self.worlds.is_empty() && configs.values().all(|(_, worlds)| worlds.is_empty()) { + Some(select_world(&resolve, None, &packages)?) + } else { + None + }; + + let registry_world_ids = registry::resolve_requested_worlds( + &mut resolve, + &packages, + ®istry_worlds, + self.registry_config, + self.default_registries, + ) + .await?; + + let worlds = registry_world_ids[..self.worlds.len()] + .iter() + .copied() + .collect::>(); - let intersector = if let Some(world) = self.intersect_world { - let intersector = select_world(&resolve, Some(world), &packages)?; + let intersector = if self.intersect_world.is_some() { + let intersector = *registry_world_ids.last().unwrap(); for &world in &worlds { intersect_world(&mut resolve, intersector, world); @@ -588,7 +639,7 @@ impl ComponentGenerator<'_> { if all_worlds.is_empty() { // No worlds specified; pick the default one, if available: - let world = select_world(&resolve, None, &packages)?; + let world = default_world.expect("the local default world was selected above"); if let Some(intersector) = intersector { intersect_world(&mut resolve, intersector, world); @@ -1036,17 +1087,6 @@ fn select_world( }) } -fn select_worlds( - resolve: &Resolve, - worlds: &[&str], - packages: &[(&Path, PackageId)], -) -> Result> { - worlds - .iter() - .map(|world| select_world(resolve, Some(world), packages)) - .collect() -} - fn union_world( resolve: &mut Resolve, name: &str, diff --git a/src/python.rs b/src/python.rs index c05ef12f..39d98f1c 100644 --- a/src/python.rs +++ b/src/python.rs @@ -18,7 +18,7 @@ use { #[allow(clippy::too_many_arguments)] #[pyo3::pyfunction] #[pyo3(name = "componentize")] -#[pyo3(signature = (wit_path, worlds, features, all_features, bindings_module, python_path, module_worlds, app_name, output_path, stub_wasi, import_interface_names, export_interface_names, full_names = None, intersect_world = None, world_module = None))] +#[pyo3(signature = (wit_path, worlds, features, all_features, bindings_module, python_path, module_worlds, app_name, output_path, stub_wasi, import_interface_names, export_interface_names, full_names = None, intersect_world = None, world_module = None, registry_config = None, no_default_registries = false))] fn python_componentize( wit_path: Vec, worlds: Vec, @@ -35,6 +35,8 @@ fn python_componentize( full_names: Option, intersect_world: Option<&str>, world_module: Option<&str>, + registry_config: Option, + no_default_registries: bool, ) -> PyResult<()> { let bindings_module = crate::resolve_deprecated( bindings_module.map(str::to_owned), @@ -72,6 +74,8 @@ fn python_componentize( .map(|(a, b)| (a.as_ref(), b.as_ref())) .collect(), intersect_world, + registry_config: registry_config.as_deref(), + default_registries: !no_default_registries, } .generate(), ) @@ -82,7 +86,7 @@ fn python_componentize( #[allow(clippy::too_many_arguments)] #[pyo3::pyfunction] #[pyo3(name = "generate_bindings")] -#[pyo3(signature = (wit_path, worlds, features, all_features, bindings_module, output_dir, import_interface_names, export_interface_names, allow_existing = false, full_names = None, world_module = None))] +#[pyo3(signature = (wit_path, worlds, features, all_features, bindings_module, output_dir, import_interface_names, export_interface_names, allow_existing = false, full_names = None, world_module = None, registry_config = None, no_default_registries = false))] fn python_generate_bindings( wit_path: Vec, worlds: Vec, @@ -95,6 +99,8 @@ fn python_generate_bindings( allow_existing: bool, full_names: Option, world_module: Option<&str>, + registry_config: Option, + no_default_registries: bool, ) -> PyResult<()> { let bindings_module = crate::resolve_deprecated( bindings_module.map(str::to_owned), @@ -103,24 +109,30 @@ fn python_generate_bindings( false, ) .map_err(|e| PyAssertionError::new_err(format!("{e:?}")))?; - BindingsGenerator { - wit_paths: &wit_path.iter().map(|v| v.as_path()).collect::>(), - worlds: &worlds.iter().map(|v| v.as_str()).collect::>(), - features: &features.iter().map(|v| v.as_str()).collect::>(), - all_features, - bindings_module: bindings_module.as_deref(), - output_dir: &output_dir, - allow_existing, - import_interface_names: &import_interface_names - .iter() - .map(|(a, b)| (a.as_ref(), b.as_ref())) - .collect(), - export_interface_names: &export_interface_names - .iter() - .map(|(a, b)| (a.as_ref(), b.as_ref())) - .collect(), - } - .generate() + (|| { + Runtime::new()?.block_on( + BindingsGenerator { + wit_paths: &wit_path.iter().map(|v| v.as_path()).collect::>(), + worlds: &worlds.iter().map(|v| v.as_str()).collect::>(), + features: &features.iter().map(|v| v.as_str()).collect::>(), + all_features, + bindings_module: bindings_module.as_deref(), + output_dir: &output_dir, + allow_existing, + import_interface_names: &import_interface_names + .iter() + .map(|(a, b)| (a.as_ref(), b.as_ref())) + .collect(), + export_interface_names: &export_interface_names + .iter() + .map(|(a, b)| (a.as_ref(), b.as_ref())) + .collect(), + registry_config: registry_config.as_deref(), + default_registries: !no_default_registries, + } + .generate(), + ) + })() .map_err(|e| PyAssertionError::new_err(format!("{e:?}"))) } diff --git a/src/registry.rs b/src/registry.rs new file mode 100644 index 00000000..699abd3f --- /dev/null +++ b/src/registry.rs @@ -0,0 +1,209 @@ +use { + anyhow::{Context, Result, bail, ensure}, + futures::TryStreamExt as _, + std::{collections::HashSet, path::Path}, + wasm_pkg_client::{Client, Config, PackageRef, Version}, + wit_component::DecodedWasm, + wit_parser::{PackageId, ParsedUsePath, Resolve, WorldId, parse_use_path}, +}; + +#[derive(Debug)] +struct RemoteWorld<'a> { + requested: &'a str, + package: PackageRef, + version: Version, + world: String, +} + +pub(crate) fn is_qualified_world_reference(world: &str) -> bool { + matches!(parse_use_path(world), Ok(ParsedUsePath::Package(_, _))) + || world + .split_once('/') + .is_some_and(|(package, _)| package.contains(':')) +} + +fn parse_remote_world(world: &str) -> Result>> { + let parsed = parse_use_path(world) + .with_context(|| format!("failed to parse world specifier `{world}`"))?; + let ParsedUsePath::Package(package, world_name) = parsed else { + return Ok(None); + }; + let Some(version) = package.version else { + bail!("remote world reference `{world}` must include an exact version"); + }; + let package_ref = format!("{}:{}", package.namespace, package.name); + + Ok(Some(RemoteWorld { + requested: world, + package: package_ref + .parse() + .with_context(|| format!("invalid package in remote world reference `{world}`"))?, + version, + world: world_name, + })) +} + +fn has_top_level_package( + resolve: &Resolve, + packages: &[(&Path, PackageId)], + remote: &RemoteWorld<'_>, +) -> bool { + packages.iter().any(|(_, package)| { + let package = &resolve.packages[*package]; + package.name.namespace == remote.package.namespace().to_string() + && package.name.name == remote.package.name().to_string() + && package.name.version.as_ref() == Some(&remote.version) + }) +} + +async fn load_config(path: Option<&Path>, default_registries: bool) -> Result { + let mut config = if default_registries { + Config::global_defaults() + .await + .context("failed to load default registry configuration")? + } else { + Config::empty() + }; + if let Some(path) = path { + config.merge(Config::from_file(path).await.with_context(|| { + format!("failed to load registry configuration `{}`", path.display()) + })?); + } + Ok(config) +} + +async fn fetch_package(client: &Client, remote: &RemoteWorld<'_>) -> Result { + let registry = client.config().resolve_registry(&remote.package).cloned(); + let source = registry + .as_ref() + .map(ToString::to_string) + .unwrap_or_else(|| "".to_owned()); + let context = || { + format!( + "failed to resolve remote world `{}` from package `{}@{}` using registry `{source}`", + remote.requested, remote.package, remote.version + ) + }; + + let release = client + .get_release(&remote.package, &remote.version) + .await + .with_context(context)?; + let mut stream = client + .stream_content(&remote.package, &release) + .await + .with_context(context)?; + let mut bytes = Vec::new(); + while let Some(chunk) = stream.try_next().await.with_context(context)? { + bytes.extend_from_slice(&chunk); + } + + let decoded = wit_component::decode(&bytes).map_err(|error| { + anyhow::anyhow!( + "{}: registry content is not a binary WIT package: {error:#}", + context() + ) + })?; + let DecodedWasm::WitPackage(resolve, package) = decoded else { + bail!( + "{}: registry content is not a binary WIT package", + context() + ); + }; + let actual = &resolve.packages[package].name; + ensure!( + actual.namespace == remote.package.namespace().to_string() + && actual.name == remote.package.name().to_string() + && actual.version.as_ref() == Some(&remote.version), + "{}: registry content declares package `{actual}`", + context() + ); + Ok(resolve) +} + +pub(crate) async fn resolve_requested_worlds( + resolve: &mut Resolve, + packages: &[(&Path, PackageId)], + worlds: &[&str], + registry_config: Option<&Path>, + default_registries: bool, +) -> Result> { + enum RequestedWorld<'a> { + Local(WorldId), + Remote(RemoteWorld<'a>), + } + + let mut requested = Vec::with_capacity(worlds.len()); + for &world in worlds { + match super::select_world(resolve, Some(world), packages) { + Ok(world) => requested.push(RequestedWorld::Local(world)), + Err(local_error) => match parse_remote_world(world)? { + Some(remote) => { + if has_top_level_package(resolve, packages, &remote) { + bail!( + "local package `{}@{}` does not contain world `{}` requested by `{}`", + remote.package, + remote.version, + remote.world, + remote.requested + ); + } + requested.push(RequestedWorld::Remote(remote)); + } + None => return Err(local_error), + }, + } + } + + let mut client = None; + let mut resolved = Vec::with_capacity(requested.len()); + let mut fetched_main_packages = HashSet::new(); + + for requested in requested { + let remote = match requested { + RequestedWorld::Local(world) => { + resolved.push(world); + continue; + } + RequestedWorld::Remote(remote) => remote, + }; + if let Ok(world) = super::select_world(resolve, Some(remote.requested), packages) { + resolved.push(world); + continue; + } + let package_key = (remote.package.clone(), remote.version.clone()); + if fetched_main_packages.contains(&package_key) { + bail!( + "registry package `{}@{}` does not contain world `{}` requested by `{}`", + remote.package, + remote.version, + remote.world, + remote.requested + ); + } + + if client.is_none() { + client = Some(Client::new( + load_config(registry_config, default_registries).await?, + )); + } + let client = client.as_ref().unwrap(); + let downloaded = fetch_package(client, &remote).await?; + resolve.merge(downloaded).with_context(|| { + format!( + "failed to merge registry package `{}@{}` for `{}`", + remote.package, remote.version, remote.requested + ) + })?; + fetched_main_packages.insert(package_key); + resolved.push( + super::select_world(resolve, Some(remote.requested), packages).with_context(|| { + format!( + "registry package `{}@{}` does not contain world `{}` requested by `{}`", + remote.package, remote.version, remote.world, remote.requested + ) + })?, + ); + } + Ok(resolved) +} diff --git a/src/test.rs b/src/test.rs index 942b7bd2..d5e88df4 100644 --- a/src/test.rs +++ b/src/test.rs @@ -85,6 +85,8 @@ async fn make_component( import_interface_names: &HashMap::new(), export_interface_names: &HashMap::new(), intersect_world, + registry_config: None, + default_registries: true, } .generate() .await?; diff --git a/src/test/tests.rs b/src/test/tests.rs index fe90903d..6929fa29 100644 --- a/src/test/tests.rs +++ b/src/test/tests.rs @@ -1063,6 +1063,8 @@ fn componentize_app( import_interface_names: &std::collections::HashMap::new(), export_interface_names: &std::collections::HashMap::new(), intersect_world: None, + registry_config: None, + default_registries: true, } .generate(), ) diff --git a/tests/bindings.rs b/tests/bindings.rs index 4b7b8e11..c45c2afe 100644 --- a/tests/bindings.rs +++ b/tests/bindings.rs @@ -10,6 +10,448 @@ use fs_extra::dir::CopyOptions; use predicates::{Predicate, prelude::predicate}; use tar::Archive; +const REGISTRY_WIT: &str = r#" +package test:registry@1.2.3; + +world command { + export registry-value: func() -> u32; +} +"#; + +fn registry_package_path(directory: &Path, package: &str, version: &str) -> PathBuf { + let (namespace, name) = package.split_once(':').unwrap(); + directory + .join("registry") + .join(namespace) + .join(name) + .join(format!("{version}.wasm")) +} + +fn write_registry_bytes( + directory: &Path, + package: &str, + version: &str, + bytes: &[u8], +) -> anyhow::Result { + let path = registry_package_path(directory, package, version); + fs::create_dir_all(path.parent().unwrap())?; + fs::write(&path, bytes)?; + Ok(path) +} + +fn write_registry_package(directory: &Path, wit: &str) -> anyhow::Result { + let mut resolve = wit_parser::Resolve::new(); + let package = resolve.push_str("registry.wit", wit)?; + let name = resolve.packages[package].name.clone(); + write_registry_bytes( + directory, + &format!("{}:{}", name.namespace, name.name), + name.version.as_ref().unwrap().to_string().as_str(), + &wit_component::encode(&resolve, package)?, + ) +} + +fn write_registry_config(directory: &Path, root: &Path) -> anyhow::Result { + let config = directory.join("registry.toml"); + fs::write( + &config, + format!( + r#"[namespace_registries] +test = "test.invalid" +wasi = "test.invalid" +ba = "test.invalid" + +[registry."test.invalid".local] +root = {:?} +"#, + root + ), + )?; + Ok(config) +} + +fn run_bindings( + directory: &Path, + config: Option<&Path>, + wit: Option<&Path>, + worlds: &[&str], + output: &str, +) -> assert_cmd::assert::Assert { + let mut command = cargo::cargo_bin_cmd!("componentize-py"); + command + .current_dir(directory) + .args(["--no-default-registries"]); + if let Some(config) = config { + command.args(["--registry-config", config.to_str().unwrap()]); + } + if let Some(wit) = wit { + command.args(["-d", wit.to_str().unwrap()]); + } + for world in worlds { + command.args(["-w", world]); + } + command.args(["bindings", output]).assert() +} + +fn dependency_packages() -> anyhow::Result<(Vec, Vec)> { + const B_WIT: &str = r#" +package test:b@1.0.0; + +interface api { + call: func(); +} + +world b-world { + export b-value: func(); +} +"#; + let sources = tempfile::tempdir()?; + fs::create_dir_all(sources.path().join("deps/b"))?; + fs::write( + sources.path().join("a.wit"), + "package test:a@1.0.0;\nworld a-world {\n import test:b/api@1.0.0;\n export a-value: func();\n}\n", + )?; + fs::write(sources.path().join("deps/b/b.wit"), B_WIT)?; + + let mut resolve = wit_parser::Resolve::new(); + let a = resolve.push_path(sources.path())?.0; + let a = wit_component::encode(&resolve, a)?; + let mut resolve = wit_parser::Resolve::new(); + let b = resolve.push_str("b.wit", B_WIT)?; + let b = wit_component::encode(&resolve, b)?; + Ok((a, b)) +} + +#[test] +fn resolves_exact_world_from_registry_without_local_wit() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + write_registry_package(dir.path(), REGISTRY_WIT)?; + let config = write_registry_config(dir.path(), &dir.path().join("registry"))?; + + run_bindings( + dir.path(), + Some(&config), + None, + &["test:registry/command@1.2.3"], + "output", + ) + .success(); + + let generated = fs::read_to_string(dir.path().join("output/wit/__init__.py"))?; + assert!( + generated.contains("def registry_value(self) -> int:"), + "{generated}" + ); + Ok(()) +} + +#[test] +fn local_world_skips_malformed_registry_config() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + let local = dir.path().join("local.wit"); + fs::write(&local, REGISTRY_WIT)?; + let config = dir.path().join("invalid.toml"); + fs::write(&config, "namespace_registries = [")?; + + run_bindings( + dir.path(), + Some(&config), + Some(&local), + &["test:registry/command@1.2.3"], + "output", + ) + .success(); + Ok(()) +} + +#[test] +fn local_and_remote_worlds_resolve_in_both_orders() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + write_registry_package(dir.path(), REGISTRY_WIT)?; + let config = write_registry_config(dir.path(), &dir.path().join("registry"))?; + let local = dir.path().join("local.wit"); + fs::write( + &local, + "package test:local@1.0.0;\nworld local { export local-value: func() -> u32; }", + )?; + + for (output, worlds) in [ + ("local-first", ["local", "test:registry/command@1.2.3"]), + ("remote-first", ["test:registry/command@1.2.3", "local"]), + ] { + run_bindings(dir.path(), Some(&config), Some(&local), &worlds, output).success(); + let generated = fs::read_to_string(dir.path().join(output).join("wit/__init__.py"))?; + assert!( + generated.contains("def local_value(self) -> int:"), + "{generated}" + ); + assert!( + generated.contains("def registry_value(self) -> int:"), + "{generated}" + ); + } + Ok(()) +} + +#[test] +fn local_unversioned_world_stays_local_after_remote_load() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + write_registry_package( + dir.path(), + "package test:registry@2.0.0;\nworld command { export remote-value: func(); }", + )?; + let config = write_registry_config(dir.path(), &dir.path().join("registry"))?; + let local = dir.path().join("local.wit"); + fs::write( + &local, + "package test:registry@1.2.3;\nworld command { export local-value: func(); }", + )?; + + for (output, worlds) in [ + ( + "local-version-first", + ["test:registry/command", "test:registry/command@2.0.0"], + ), + ( + "remote-version-first", + ["test:registry/command@2.0.0", "test:registry/command"], + ), + ] { + run_bindings(dir.path(), Some(&config), Some(&local), &worlds, output).success(); + let generated = fs::read_to_string(dir.path().join(output).join("wit/__init__.py"))?; + assert!( + generated.contains("def local_value(self) -> None:"), + "{generated}" + ); + assert!( + generated.contains("def remote_value(self) -> None:"), + "{generated}" + ); + } + Ok(()) +} + +#[test] +fn short_and_unversioned_remote_only_names_are_rejected() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + fs::create_dir_all(dir.path().join("wit"))?; + fs::write( + dir.path().join("wit/local.wit"), + "package test:local@1.0.0;\nworld local {}", + )?; + let config = write_registry_config(dir.path(), &dir.path().join("missing"))?; + + run_bindings(dir.path(), Some(&config), None, &["command"], "short") + .failure() + .stderr(predicate::str::contains("Unable to resolve")); + run_bindings( + dir.path(), + Some(&config), + None, + &["test:registry/command"], + "unversioned", + ) + .failure() + .stderr(predicate::str::contains("must include an exact version")); + Ok(()) +} + +#[test] +fn malformed_qualified_name_reports_parse_error() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + run_bindings( + dir.path(), + None, + None, + &["test:registry/command@"], + "output", + ) + .failure() + .stderr(predicate::str::contains("failed to parse world specifier")); + Ok(()) +} + +#[test] +fn local_package_missing_world_is_not_replaced() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + let local = dir.path().join("local.wit"); + fs::write( + &local, + "package test:registry@1.2.3;\nworld other { export value: func(); }", + )?; + let config = write_registry_config(dir.path(), &dir.path().join("missing"))?; + + run_bindings( + dir.path(), + Some(&config), + Some(&local), + &["test:registry/command@1.2.3"], + "output", + ) + .failure() + .stderr(predicate::str::contains( + "local package `test:registry@1.2.3` does not contain world `command`", + )); + Ok(()) +} + +#[test] +fn dependency_packages_resolve_without_replacing_explicit_remote_world() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + let (a, b) = dependency_packages()?; + let local_a = dir.path().join("a.wasm"); + fs::write(&local_a, a)?; + write_registry_bytes(dir.path(), "test:b", "1.0.0", &b)?; + let config = write_registry_config(dir.path(), &dir.path().join("registry"))?; + + run_bindings( + dir.path(), + Some(&config), + Some(&local_a), + &["test:a/a-world@1.0.0", "test:b/b-world@1.0.0"], + "output", + ) + .success(); + let generated = fs::read_to_string(dir.path().join("output/wit/__init__.py"))?; + assert!( + generated.contains("def a_value(self) -> None:"), + "{generated}" + ); + assert!( + generated.contains("def b_value(self) -> None:"), + "{generated}" + ); + Ok(()) +} + +#[test] +fn remote_dependency_packages_resolve_in_both_orders() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + let (a, b) = dependency_packages()?; + write_registry_bytes(dir.path(), "test:a", "1.0.0", &a)?; + write_registry_bytes(dir.path(), "test:b", "1.0.0", &b)?; + let config = write_registry_config(dir.path(), &dir.path().join("registry"))?; + + for (output, worlds) in [ + ("a-first", ["test:a/a-world@1.0.0", "test:b/b-world@1.0.0"]), + ("b-first", ["test:b/b-world@1.0.0", "test:a/a-world@1.0.0"]), + ] { + run_bindings(dir.path(), Some(&config), None, &worlds, output).success(); + let generated = fs::read_to_string(dir.path().join(output).join("wit/__init__.py"))?; + assert!( + generated.contains("def a_value(self) -> None:"), + "{generated}" + ); + assert!( + generated.contains("def b_value(self) -> None:"), + "{generated}" + ); + } + Ok(()) +} + +#[test] +fn registry_errors_identify_missing_world_non_wit_and_wrong_package() -> anyhow::Result<()> { + let cases = [ + ( + "missing-world", + "package test:registry@1.2.3;\nworld other {}", + "registry package `test:registry@1.2.3` does not contain world `command`", + ), + ( + "non-wit", + "", + "registry content is not a binary WIT package", + ), + ( + "wrong-package", + "package test:other@1.2.3;\nworld command {}", + "registry content declares package `test:other@1.2.3`", + ), + ( + "wrong-version", + "package test:registry@2.0.0;\nworld command {}", + "registry content declares package `test:registry@2.0.0`", + ), + ]; + + for (output, wit, error) in cases { + let dir = tempfile::tempdir()?; + if wit.is_empty() { + write_registry_bytes(dir.path(), "test:registry", "1.2.3", b"not a wasm package")?; + } else { + write_registry_package(dir.path(), wit)?; + if output == "wrong-package" { + let source = registry_package_path(dir.path(), "test:other", "1.2.3"); + let target = registry_package_path(dir.path(), "test:registry", "1.2.3"); + fs::create_dir_all(target.parent().unwrap())?; + fs::rename(source, target)?; + } else if output == "wrong-version" { + let source = registry_package_path(dir.path(), "test:registry", "2.0.0"); + let target = registry_package_path(dir.path(), "test:registry", "1.2.3"); + fs::rename(source, target)?; + } + } + let config = write_registry_config(dir.path(), &dir.path().join("registry"))?; + run_bindings( + dir.path(), + Some(&config), + None, + &["test:registry/command@1.2.3"], + output, + ) + .failure() + .stderr(predicate::str::contains(error)); + } + Ok(()) +} + +#[test] +fn configured_registry_failure_identifies_the_remote_world() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + let config = write_registry_config(dir.path(), &dir.path().join("offline"))?; + run_bindings( + dir.path(), + Some(&config), + None, + &["test:registry/command@1.2.3"], + "output", + ) + .failure() + .stderr(predicate::str::contains( + "failed to resolve remote world `test:registry/command@1.2.3`", + )); + Ok(()) +} + +#[test] +fn disabled_defaults_require_explicit_mapping() -> anyhow::Result<()> { + for world in ["wasi:registry/command@1.2.3", "ba:registry/command@1.2.3"] { + let dir = tempfile::tempdir()?; + run_bindings(dir.path(), None, None, &[world], "output") + .failure() + .stderr(predicate::str::contains( + "no registry configured for namespace", + )) + .stderr(predicate::str::contains("")); + } + + let dir = tempfile::tempdir()?; + write_registry_package( + dir.path(), + "package wasi:registry@1.2.3;\nworld command { export value: func(); }", + )?; + let config = write_registry_config(dir.path(), &dir.path().join("registry"))?; + run_bindings( + dir.path(), + Some(&config), + None, + &["wasi:registry/command@1.2.3"], + "mapped", + ) + .success(); + Ok(()) +} #[test] fn lint_cli_bindings() -> anyhow::Result<()> { let dir = tempfile::tempdir()?; diff --git a/tests/componentize.rs b/tests/componentize.rs index 32e9b3aa..22d792f0 100644 --- a/tests/componentize.rs +++ b/tests/componentize.rs @@ -1,5 +1,6 @@ use core::net::Ipv4Addr; use std::{ + fs, io::{Read as _, Write as _}, path::{Path, PathBuf}, process::Stdio, @@ -13,6 +14,121 @@ use fs_extra::dir::CopyOptions; use predicates::prelude::predicate; use tar::Archive; +fn write_test_registry(directory: &Path, package: &str, wit: &str) -> anyhow::Result { + let (namespace, name) = package.split_once(':').unwrap(); + let mut resolve = wit_parser::Resolve::new(); + let package_id = resolve.push_str("registry.wit", wit)?; + let version = resolve.packages[package_id] + .name + .version + .as_ref() + .unwrap() + .to_string(); + let root = directory.join("registry"); + let package_path = root + .join(namespace) + .join(name) + .join(format!("{version}.wasm")); + fs::create_dir_all(package_path.parent().unwrap())?; + fs::write(package_path, wit_component::encode(&resolve, package_id)?)?; + + let config = directory.join("registry.toml"); + fs::write( + &config, + format!( + r#"[namespace_registries] +test = "test.invalid" + +[registry."test.invalid".local] +root = {:?} +"#, + root + ), + )?; + Ok(config) +} + +#[test] +fn componentizes_remote_target_without_local_wit() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + let config = write_test_registry( + dir.path(), + "test:registry", + "package test:registry@1.2.3;\nworld command { export marker: func() -> string; }", + )?; + fs::write( + dir.path().join("app.py"), + "import wit\n\n@wit.guest\nclass App(wit.WorldExports):\n def marker(self) -> str:\n return \"registry\"\n", + )?; + + cargo::cargo_bin_cmd!("componentize-py") + .current_dir(dir.path()) + .args([ + "--no-default-registries", + "--registry-config", + config.to_str().unwrap(), + "-w", + "test:registry/command@1.2.3", + "componentize", + "app", + "-o", + "app.wasm", + ]) + .assert() + .success(); + assert!(dir.path().join("app.wasm").is_file()); + Ok(()) +} + +#[test] +fn remote_intersector_preserves_local_default_target() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + let config = write_test_registry( + dir.path(), + "test:registry", + "package test:registry@1.2.3;\nworld limiter { export marker: func() -> u32; }", + )?; + fs::write( + dir.path().join("local.wit"), + "package test:local@1.0.0;\nworld local { export marker: func() -> string; }", + )?; + fs::write( + dir.path().join("app.py"), + "import wit\n\n@wit.guest\nclass App(wit.WorldExports):\n def marker(self) -> str:\n return \"local\"\n", + )?; + + cargo::cargo_bin_cmd!("componentize-py") + .current_dir(dir.path()) + .args([ + "--no-default-registries", + "--registry-config", + config.to_str().unwrap(), + "-d", + "local.wit", + "componentize", + "app", + "--intersect-world", + "test:registry/limiter@1.2.3", + "-o", + "app.wasm", + ]) + .assert() + .success(); + + let bytes = fs::read(dir.path().join("app.wasm"))?; + let wit_component::DecodedWasm::Component(resolve, world) = wit_component::decode(&bytes)? + else { + anyhow::bail!("expected a component"); + }; + let wit_parser::WorldItem::Function(marker) = + &resolve.worlds[world].exports[&wit_parser::WorldKey::Name("marker".into())] + else { + anyhow::bail!("expected the marker function"); + }; + assert_eq!(marker.result, Some(wit_parser::Type::String)); + Ok(()) +} + #[test] fn cli_example() -> anyhow::Result<()> { test_cli_example("cli", "wasi:cli/command@0.2.0")