difftreelog
feat jrb
in: master
11 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,12 @@
version = 4
[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
+[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -109,7 +115,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -120,7 +126,7 @@
dependencies = [
"anstyle",
"once_cell_polyfill",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -139,18 +145,61 @@
]
[[package]]
+name = "arc-swap"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207"
+dependencies = [
+ "rustversion",
+]
+
+[[package]]
name = "arraydeque"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
+name = "aws-lc-rs"
+version = "1.16.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f"
+dependencies = [
+ "aws-lc-sys",
+ "zeroize",
+]
+
+[[package]]
+name = "aws-lc-sys"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7"
+dependencies = [
+ "cc",
+ "cmake",
+ "dunce",
+ "fs_extra",
+]
+
+[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -164,6 +213,15 @@
[[package]]
name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "block-buffer"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be"
@@ -178,6 +236,7 @@
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
dependencies = [
"memchr",
+ "regex-automata",
"serde",
]
@@ -191,6 +250,33 @@
]
[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
+
+[[package]]
+name = "bytesize"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3"
+
+[[package]]
+name = "camino"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -203,6 +289,8 @@
checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
dependencies = [
"find-msvc-tools",
+ "jobserver",
+ "libc",
"shlex",
]
@@ -295,12 +383,40 @@
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
+name = "clru"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "197fd99cb113a8d5d9b6376f3aa817f32c1078f2343b714fff7d2ca44fdf67d5"
+dependencies = [
+ "hashbrown 0.16.1",
+]
+
+[[package]]
+name = "cmake"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
+dependencies = [
+ "cc",
+]
+
+[[package]]
name = "colorchoice"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
name = "console"
version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -308,7 +424,7 @@
dependencies = [
"encode_unicode",
"libc",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -328,6 +444,22 @@
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[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 = "countme"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -335,6 +467,15 @@
[[package]]
name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
@@ -343,6 +484,15 @@
]
[[package]]
+name = "crc32fast"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
name = "criterion"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -378,6 +528,15 @@
]
[[package]]
+name = "crossbeam-channel"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -410,6 +569,16 @@
[[package]]
name = "crypto-common"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "crypto-common"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710"
@@ -418,17 +587,62 @@
]
[[package]]
+name = "dashmap"
+version = "6.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+ "hashbrown 0.14.5",
+ "lock_api",
+ "once_cell",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "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",
+ "block-buffer 0.12.0",
"const-oid",
- "crypto-common",
+ "crypto-common 0.2.1",
]
[[package]]
+name = "directories"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -461,6 +675,12 @@
checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1"
[[package]]
+name = "dunce"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+
+[[package]]
name = "educe"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -535,7 +755,7 @@
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -550,18 +770,49 @@
]
[[package]]
+name = "faster-hex"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73"
+dependencies = [
+ "heapless",
+ "serde",
+]
+
+[[package]]
name = "fastrand"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
[[package]]
+name = "filetime"
+version = "0.2.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "libredox",
+]
+
+[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
+name = "flate2"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
+dependencies = [
+ "miniz_oxide",
+ "zlib-rs",
+]
+
+[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -589,7 +840,85 @@
]
[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
+[[package]]
+name = "futures-channel"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
+
+[[package]]
+name = "futures-io"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
+
+[[package]]
+name = "futures-sink"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
+
+[[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-io",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
name = "getrandom"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
@@ -616,6 +945,940 @@
]
[[package]]
+name = "gix"
+version = "0.83.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce52001b946a6249d5d0d3011df0a042ac3f8a4d013460db6476577b0b9c567"
+dependencies = [
+ "gix-actor",
+ "gix-archive",
+ "gix-attributes",
+ "gix-blame",
+ "gix-command",
+ "gix-commitgraph",
+ "gix-config",
+ "gix-credentials",
+ "gix-date",
+ "gix-diff",
+ "gix-dir",
+ "gix-discover",
+ "gix-error",
+ "gix-features",
+ "gix-filter",
+ "gix-fs",
+ "gix-glob",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-ignore",
+ "gix-index",
+ "gix-lock",
+ "gix-mailmap",
+ "gix-merge",
+ "gix-negotiate",
+ "gix-object",
+ "gix-odb",
+ "gix-pack",
+ "gix-path",
+ "gix-pathspec",
+ "gix-prompt",
+ "gix-protocol",
+ "gix-ref",
+ "gix-refspec",
+ "gix-revision",
+ "gix-revwalk",
+ "gix-sec",
+ "gix-shallow",
+ "gix-status",
+ "gix-submodule",
+ "gix-tempfile",
+ "gix-trace",
+ "gix-transport",
+ "gix-traverse",
+ "gix-url",
+ "gix-utils",
+ "gix-validate",
+ "gix-worktree",
+ "gix-worktree-state",
+ "gix-worktree-stream",
+ "nonempty",
+ "parking_lot",
+ "regex",
+ "signal-hook",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-actor"
+version = "0.41.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "272916673b83714734b15d4ef3c8b5f1ccddb15fea8ff548430b97c1ab7b7ed8"
+dependencies = [
+ "bstr",
+ "gix-date",
+ "gix-error",
+]
+
+[[package]]
+name = "gix-archive"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a20ec244b733338d4cb60e5e05eac700dab7fcc689647b1d1daa9396b119342"
+dependencies = [
+ "bstr",
+ "gix-date",
+ "gix-error",
+ "gix-object",
+ "gix-worktree-stream",
+]
+
+[[package]]
+name = "gix-attributes"
+version = "0.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe17c5a1c0b6f2ef1476aa1d3222ea50cdff67608016613a58bfc3e078046000"
+dependencies = [
+ "bstr",
+ "gix-glob",
+ "gix-path",
+ "gix-quote",
+ "gix-trace",
+ "kstring",
+ "smallvec",
+ "thiserror",
+ "unicode-bom",
+]
+
+[[package]]
+name = "gix-bitmap"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ecbfc77ec6852294e341ecc305a490b59f2813e6ca42d79efda5099dcab1894"
+dependencies = [
+ "gix-error",
+]
+
+[[package]]
+name = "gix-blame"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14dab9a942ab54a9661ded7397c3bf927274e7afa94494db0d75cfcbde02ca0a"
+dependencies = [
+ "gix-commitgraph",
+ "gix-date",
+ "gix-diff",
+ "gix-error",
+ "gix-hash",
+ "gix-object",
+ "gix-revwalk",
+ "gix-trace",
+ "gix-traverse",
+ "gix-worktree",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-chunk"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edf288be9b60fe7231de03771faa292be1493d84786f68727e33ad1f91764320"
+dependencies = [
+ "gix-error",
+]
+
+[[package]]
+name = "gix-command"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86335306511abe43d75c866d4b1f3d90932fe202edcd43e1314036333e7384d8"
+dependencies = [
+ "bstr",
+ "gix-path",
+ "gix-quote",
+ "gix-trace",
+ "shell-words",
+]
+
+[[package]]
+name = "gix-commitgraph"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe3b5aa0f24e19028c261d229aeeedafcaaa52ebd71021cc15184620fc9d32eb"
+dependencies = [
+ "bstr",
+ "gix-chunk",
+ "gix-error",
+ "gix-hash",
+ "memmap2",
+ "nonempty",
+]
+
+[[package]]
+name = "gix-config"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c01848aebd21c67f6ba41f1de8efd46ae96df21f001954a3c9e1517e514d410"
+dependencies = [
+ "bstr",
+ "gix-config-value",
+ "gix-features",
+ "gix-glob",
+ "gix-path",
+ "gix-ref",
+ "gix-sec",
+ "smallvec",
+ "thiserror",
+ "unicode-bom",
+]
+
+[[package]]
+name = "gix-config-value"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13b39ed39ee4c10a3b157f9fb94bac8098d9f8e56201f0cf7dee6c187416c4b2"
+dependencies = [
+ "bitflags",
+ "bstr",
+ "gix-path",
+ "libc",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-credentials"
+version = "0.38.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65ca11598b70811d7b16ff90945a6e57dfe521e85b744e51636965fe39cc8f60"
+dependencies = [
+ "bstr",
+ "gix-command",
+ "gix-config-value",
+ "gix-date",
+ "gix-path",
+ "gix-prompt",
+ "gix-sec",
+ "gix-trace",
+ "gix-url",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-date"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b94cdae4eb4b0f4136e3d9b3aa2d2cd03cfb5bb9b636b31263aea2df86d41543"
+dependencies = [
+ "bstr",
+ "gix-error",
+ "itoa",
+ "jiff",
+ "smallvec",
+]
+
+[[package]]
+name = "gix-diff"
+version = "0.63.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc08e0fa1a91ff5f24affeab052f198056645e1de004910bde7b82b50ea5982a"
+dependencies = [
+ "bstr",
+ "gix-attributes",
+ "gix-command",
+ "gix-filter",
+ "gix-fs",
+ "gix-hash",
+ "gix-imara-diff",
+ "gix-index",
+ "gix-object",
+ "gix-path",
+ "gix-pathspec",
+ "gix-tempfile",
+ "gix-trace",
+ "gix-traverse",
+ "gix-worktree",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-dir"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a0fc06e9e1e430cbf0a313666976d90f822f461a6525320427aa9b8af5236c"
+dependencies = [
+ "bstr",
+ "gix-discover",
+ "gix-fs",
+ "gix-ignore",
+ "gix-index",
+ "gix-object",
+ "gix-path",
+ "gix-pathspec",
+ "gix-trace",
+ "gix-utils",
+ "gix-worktree",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-discover"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17852e6a501e688a1702b24ebe5b3761d4719455bc869fd29f38b0b859bcad34"
+dependencies = [
+ "bstr",
+ "dunce",
+ "gix-fs",
+ "gix-path",
+ "gix-ref",
+ "gix-sec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-error"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e207b971746ab724fccdfced2e4e19e854744611904a0195d3aa8fda8a110613"
+dependencies = [
+ "bstr",
+]
+
+[[package]]
+name = "gix-features"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af375693ad5333d0a2c66b4c5b2cbe9ccc38e34f8e8bf24e4ae42c12307fdc4f"
+dependencies = [
+ "bytes",
+ "bytesize",
+ "crc32fast",
+ "crossbeam-channel",
+ "gix-path",
+ "gix-trace",
+ "gix-utils",
+ "libc",
+ "once_cell",
+ "parking_lot",
+ "prodash",
+ "thiserror",
+ "walkdir",
+ "zlib-rs",
+]
+
+[[package]]
+name = "gix-filter"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dac917dbe9653c9b615d248db91907a365bd779750c9e1b457a9d9fdeece3a08"
+dependencies = [
+ "bstr",
+ "encoding_rs",
+ "gix-attributes",
+ "gix-command",
+ "gix-hash",
+ "gix-object",
+ "gix-packetline",
+ "gix-path",
+ "gix-quote",
+ "gix-trace",
+ "gix-utils",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-fs"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e1967daac9848757c47c2aef0c57bcadc1a897347f559778249bf286a536c86"
+dependencies = [
+ "bstr",
+ "fastrand",
+ "gix-features",
+ "gix-path",
+ "gix-utils",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-glob"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08bf29249a069bf2507f5964f80997f37b134d320ea348d66527726b9be2c38c"
+dependencies = [
+ "bitflags",
+ "bstr",
+ "gix-features",
+ "gix-path",
+]
+
+[[package]]
+name = "gix-hash"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcf70d1e252337eed16360f8b8ebb71865ece58eab7954b39ce38b420de703d2"
+dependencies = [
+ "faster-hex",
+ "gix-features",
+ "sha1-checked",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-hashtable"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d33b455e07b3c16d3b2eeebc7b38d2dafcbf8a653de1138ef55d4c2a1fd0b08b"
+dependencies = [
+ "gix-hash",
+ "hashbrown 0.16.1",
+ "parking_lot",
+]
+
+[[package]]
+name = "gix-ignore"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb13fbbeeafee943e52b61fcc88dfddf6a452fcaf0c4d0cdc8f218fa25bbec5"
+dependencies = [
+ "bstr",
+ "gix-glob",
+ "gix-path",
+ "gix-trace",
+ "unicode-bom",
+]
+
+[[package]]
+name = "gix-imara-diff"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39eb0623e15e4cb83c02ce6a959e48fadd1ae3b715b36b5acc01816e01388c82"
+dependencies = [
+ "bstr",
+ "hashbrown 0.16.1",
+]
+
+[[package]]
+name = "gix-index"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54c3ef97ad08121e4327a6226bd63fed6b9e3c6b976d48bddd4356d9d41191db"
+dependencies = [
+ "bitflags",
+ "bstr",
+ "filetime",
+ "fnv",
+ "gix-bitmap",
+ "gix-features",
+ "gix-fs",
+ "gix-hash",
+ "gix-lock",
+ "gix-object",
+ "gix-traverse",
+ "gix-utils",
+ "gix-validate",
+ "hashbrown 0.16.1",
+ "itoa",
+ "libc",
+ "memmap2",
+ "rustix",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-lock"
+version = "23.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09b3bc074e5723027b482dcd9ab99d95804a53742f6de812d0172fbba4a186c1"
+dependencies = [
+ "gix-tempfile",
+ "gix-utils",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-mailmap"
+version = "0.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "023d3a6561cbebe45b89e0764d48928ad970667076f16fa5889e6f86d8432086"
+dependencies = [
+ "bstr",
+ "gix-actor",
+ "gix-date",
+ "gix-error",
+]
+
+[[package]]
+name = "gix-merge"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74bbcdcc52b70a32f0a151b024dff9d0fcf56ee48f00d9503e735af9d99ea881"
+dependencies = [
+ "bstr",
+ "gix-command",
+ "gix-diff",
+ "gix-filter",
+ "gix-fs",
+ "gix-hash",
+ "gix-imara-diff",
+ "gix-index",
+ "gix-object",
+ "gix-path",
+ "gix-quote",
+ "gix-revision",
+ "gix-revwalk",
+ "gix-tempfile",
+ "gix-trace",
+ "gix-worktree",
+ "nonempty",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-negotiate"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "103d42bfade1b8a96ca5005933127bdad461ce588d92422b2c2daa3ff20d780c"
+dependencies = [
+ "bitflags",
+ "gix-commitgraph",
+ "gix-date",
+ "gix-hash",
+ "gix-object",
+ "gix-revwalk",
+]
+
+[[package]]
+name = "gix-object"
+version = "0.60.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a38075a95d7cc5df8afd38e72c617026c1456952207a4120a7f55a3fbf93b4d7"
+dependencies = [
+ "bstr",
+ "gix-actor",
+ "gix-date",
+ "gix-features",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-utils",
+ "gix-validate",
+ "itoa",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-odb"
+version = "0.80.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aeeda12a9663120418735ecdc1250d06eeab0be75700e47b3402a981331716ba"
+dependencies = [
+ "arc-swap",
+ "gix-features",
+ "gix-fs",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-object",
+ "gix-pack",
+ "gix-path",
+ "gix-quote",
+ "memmap2",
+ "parking_lot",
+ "tempfile",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-pack"
+version = "0.70.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daf02e6f5c8f07a069c9ea5245f40d9b14856ada4086091dc99941b49002b4fa"
+dependencies = [
+ "clru",
+ "gix-chunk",
+ "gix-error",
+ "gix-features",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-object",
+ "gix-path",
+ "gix-tempfile",
+ "memmap2",
+ "parking_lot",
+ "smallvec",
+ "thiserror",
+ "uluru",
+]
+
+[[package]]
+name = "gix-packetline"
+version = "0.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "362246df440ee691699f0664cbf7006a6ece477db6734222be95e4198e5656e6"
+dependencies = [
+ "bstr",
+ "faster-hex",
+ "gix-trace",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-path"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "671a6059e8a4c1b7f406e24716499cefa3926e060876fb1959ef225efeee346e"
+dependencies = [
+ "bstr",
+ "gix-trace",
+ "gix-validate",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-pathspec"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a84a4f083dd70fb49f4377e13afa6d90df2daaa1c705c49d6ff1331fc7e8855"
+dependencies = [
+ "bitflags",
+ "bstr",
+ "gix-attributes",
+ "gix-config-value",
+ "gix-glob",
+ "gix-path",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-prompt"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e041a626c64cb69e4117fcdf80da8d0e454fba3b1f420412792d191f52251aee"
+dependencies = [
+ "gix-command",
+ "gix-config-value",
+ "parking_lot",
+ "rustix",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-protocol"
+version = "0.61.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa4bee82db63ec635996b96efae71cf467c155fa3f34a556184373224a26c4fd"
+dependencies = [
+ "bstr",
+ "gix-credentials",
+ "gix-date",
+ "gix-features",
+ "gix-hash",
+ "gix-lock",
+ "gix-negotiate",
+ "gix-object",
+ "gix-ref",
+ "gix-refspec",
+ "gix-revwalk",
+ "gix-shallow",
+ "gix-trace",
+ "gix-transport",
+ "gix-utils",
+ "maybe-async",
+ "nonempty",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-quote"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e97b73791a64bc0fa7dd2c5b3e551136115f97750b876ed1c952c7a7dbaf8be"
+dependencies = [
+ "bstr",
+ "gix-error",
+ "gix-utils",
+]
+
+[[package]]
+name = "gix-ref"
+version = "0.63.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8ba9cc15f558b274c99349b83130f5ec83459660828fde9718bbbb43a726167"
+dependencies = [
+ "gix-actor",
+ "gix-features",
+ "gix-fs",
+ "gix-hash",
+ "gix-lock",
+ "gix-object",
+ "gix-path",
+ "gix-tempfile",
+ "gix-utils",
+ "gix-validate",
+ "memmap2",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-refspec"
+version = "0.41.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61755b27d57edc8940a1b1593c8c61548ca8e4c02da1ed8d5bfeda9eb2a6b761"
+dependencies = [
+ "bstr",
+ "gix-error",
+ "gix-glob",
+ "gix-hash",
+ "gix-revision",
+ "gix-validate",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-revision"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fb5288fac706d3ea3e4e2ba9ec38b78743b8c02f422e18cb342299cfd6ab7e8"
+dependencies = [
+ "bitflags",
+ "bstr",
+ "gix-commitgraph",
+ "gix-date",
+ "gix-error",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-object",
+ "gix-revwalk",
+ "gix-trace",
+ "nonempty",
+]
+
+[[package]]
+name = "gix-revwalk"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "313813706b073a12ff7f9b2896bf3e6504cdac7cfbc97b1920114724705069f0"
+dependencies = [
+ "gix-commitgraph",
+ "gix-date",
+ "gix-error",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-object",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-sec"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5a3a2d3e504a238136751e646a6c028252286a0ea64ea9974bf0498633407c6"
+dependencies = [
+ "bitflags",
+ "gix-path",
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "gix-shallow"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29187305521bfacf4aefd284ab28dbfa9fb74abd39a5e63dd313b1baa5808c27"
+dependencies = [
+ "bstr",
+ "gix-hash",
+ "gix-lock",
+ "nonempty",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-status"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68c6d2a8c521ffa205fe7e268c82e6d1378ba37cd826ca10ab6129fdc29a4b65"
+dependencies = [
+ "bstr",
+ "filetime",
+ "gix-diff",
+ "gix-dir",
+ "gix-features",
+ "gix-filter",
+ "gix-fs",
+ "gix-hash",
+ "gix-index",
+ "gix-object",
+ "gix-path",
+ "gix-pathspec",
+ "gix-worktree",
+ "portable-atomic",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-submodule"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fd5fc8692890bd71a596e540fd4c364f8460eaa82c4eaaedebde6e1e3eb4d91"
+dependencies = [
+ "bstr",
+ "gix-config",
+ "gix-path",
+ "gix-pathspec",
+ "gix-refspec",
+ "gix-url",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-tempfile"
+version = "23.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "691ea1e31435c7e7d4d04705ec9d1c0d9482c46b2acf512bc723939d8f0af7fb"
+dependencies = [
+ "dashmap",
+ "gix-fs",
+ "libc",
+ "parking_lot",
+ "signal-hook",
+ "signal-hook-registry",
+ "tempfile",
+]
+
+[[package]]
+name = "gix-trace"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f23569e55f2ffaf958617353b9734a7d52a7c19c439eeaa5e3efc217fd2270e"
+
+[[package]]
+name = "gix-transport"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffd6a5c676b92d4ead5f5a2b2935024415dec69edc997b6090ca9cac010a3018"
+dependencies = [
+ "base64",
+ "bstr",
+ "gix-command",
+ "gix-credentials",
+ "gix-features",
+ "gix-packetline",
+ "gix-quote",
+ "gix-sec",
+ "gix-url",
+ "reqwest",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-traverse"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a14b7052c0786676c03e71fcfde7d7f0f8e8316e642b5cec6bb3998719b2ce5c"
+dependencies = [
+ "bitflags",
+ "gix-commitgraph",
+ "gix-date",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-object",
+ "gix-revwalk",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-url"
+version = "0.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35842d099e813f6f6bba529e88d4670572149c3df79b7a412952259887721ece"
+dependencies = [
+ "bstr",
+ "gix-path",
+ "percent-encoding",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-utils"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e477b4f07a6e8da4ba791c53c858102959703c60d70f199932010d5b94adb2c"
+dependencies = [
+ "bstr",
+ "fastrand",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "gix-validate"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e26ac2602b43eadfdca0560b81d3341944162a3c9f64ccdeef8fc501ad80dad5"
+dependencies = [
+ "bstr",
+]
+
+[[package]]
+name = "gix-worktree"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d69955eb5e2910832f88d041964b809eee01dadd579237e0b55efec58fd406fd"
+dependencies = [
+ "bstr",
+ "gix-attributes",
+ "gix-fs",
+ "gix-glob",
+ "gix-hash",
+ "gix-ignore",
+ "gix-index",
+ "gix-object",
+ "gix-path",
+ "gix-validate",
+]
+
+[[package]]
+name = "gix-worktree-state"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a96dccbcf9e8fe0291c55f06e08da93ebb2e691c1311276f541eefcc6d70800"
+dependencies = [
+ "bstr",
+ "gix-features",
+ "gix-filter",
+ "gix-fs",
+ "gix-index",
+ "gix-object",
+ "gix-path",
+ "gix-worktree",
+ "io-close",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-worktree-stream"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8444b8ed4662e1a0c97f3eceda29630001a1bbb2632201e50312623e594213"
+dependencies = [
+ "gix-attributes",
+ "gix-error",
+ "gix-features",
+ "gix-filter",
+ "gix-fs",
+ "gix-hash",
+ "gix-object",
+ "gix-path",
+ "gix-traverse",
+ "parking_lot",
+]
+
+[[package]]
name = "globset"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -639,6 +1902,25 @@
]
[[package]]
+name = "h2"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
name = "half"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -650,6 +1932,15 @@
]
[[package]]
+name = "hash32"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -668,6 +1959,17 @@
[[package]]
name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+dependencies = [
+ "allocator-api2",
+ "equivalent",
+ "foldhash 0.2.0",
+]
+
+[[package]]
+name = "hashbrown"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
@@ -678,6 +1980,16 @@
]
[[package]]
+name = "heapless"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
+dependencies = [
+ "hash32",
+ "stable_deref_trait",
+]
+
+[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -723,6 +2035,51 @@
]
[[package]]
+name = "http"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
+dependencies = [
+ "bytes",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+
+[[package]]
+name = "human_format"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaec953f16e5bcf6b8a3cb3aa959b17e5577dbd2693e94554c462c08be22624b"
+
+[[package]]
name = "hybrid-array"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -732,6 +2089,65 @@
]
[[package]]
+name = "hyper"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.27.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
+dependencies = [
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
+dependencies = [
+ "base64",
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "ipnet",
+ "libc",
+ "percent-encoding",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
name = "icu_collections"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -876,6 +2292,22 @@
]
[[package]]
+name = "io-close"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
+
+[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -906,6 +2338,118 @@
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
+name = "jiff"
+version = "0.2.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d"
+dependencies = [
+ "jiff-static",
+ "jiff-tzdb-platform",
+ "log",
+ "portable-atomic",
+ "portable-atomic-util",
+ "serde_core",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "jiff-static"
+version = "0.2.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "jiff-tzdb"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076"
+
+[[package]]
+name = "jiff-tzdb-platform"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
+dependencies = [
+ "jiff-tzdb",
+]
+
+[[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",
+ "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",
+]
+
+[[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",
+]
+
+[[package]]
+name = "jobserver"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
+dependencies = [
+ "getrandom 0.3.4",
+ "libc",
+]
+
+[[package]]
+name = "jrb"
+version = "0.5.0-pre98"
+dependencies = [
+ "clap",
+ "jrsonnet-pkg",
+ "serde",
+ "serde_json",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
name = "jrsonnet"
version = "0.5.0-pre98"
dependencies = [
@@ -1078,6 +2622,23 @@
]
[[package]]
+name = "jrsonnet-pkg"
+version = "0.5.0-pre98"
+dependencies = [
+ "camino",
+ "directories",
+ "gix",
+ "peg",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tracing",
+ "url",
+ "zip",
+]
+
+[[package]]
name = "jrsonnet-rowan-parser"
version = "0.5.0-pre98"
dependencies = [
@@ -1109,7 +2670,7 @@
"serde",
"serde-saphyr",
"serde_json",
- "sha1",
+ "sha1 0.11.0",
"sha2",
"sha3",
]
@@ -1158,10 +2719,25 @@
checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa"
dependencies = [
"cfg-if",
- "cpufeatures",
+ "cpufeatures 0.3.0",
+]
+
+[[package]]
+name = "kstring"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1"
+dependencies = [
+ "static_assertions",
]
[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1185,6 +2761,18 @@
]
[[package]]
+name = "libredox"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c"
+dependencies = [
+ "bitflags",
+ "libc",
+ "plain",
+ "redox_syscall 0.7.5",
+]
+
+[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1197,6 +2785,15 @@
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]]
+name = "lock_api"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1244,6 +2841,32 @@
]
[[package]]
+name = "lru-slab"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
+
+[[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-async"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "md5"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1256,6 +2879,15 @@
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
+name = "memmap2"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "mimalloc-sys"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1275,6 +2907,33 @@
]
[[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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
+[[package]]
+name = "mio"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
name = "nix"
version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1287,6 +2946,21 @@
]
[[package]]
+name = "nonempty"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9737e026353e5cd0736f98eddae28665118eb6f6600902a7f50db585621fecb6"
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.50.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1343,6 +3017,18 @@
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
[[package]]
+name = "openssl-probe"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
+
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
name = "ouroboros"
version = "0.18.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1377,6 +3063,29 @@
]
[[package]]
+name = "parking_lot"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.5.18",
+ "smallvec",
+ "windows-link",
+]
+
+[[package]]
name = "pathdiff"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1416,6 +3125,18 @@
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
+[[package]]
+name = "plain"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
+
+[[package]]
name = "plotters"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1444,6 +3165,21 @@
]
[[package]]
+name = "portable-atomic"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
+dependencies = [
+ "portable-atomic",
+]
+
+[[package]]
name = "potential_utf"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1494,6 +3230,17 @@
]
[[package]]
+name = "prodash"
+version = "31.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "962200e2d7d551451297d9fdce85138374019ada198e30ea9ede38034e27604c"
+dependencies = [
+ "bytesize",
+ "human_format",
+ "parking_lot",
+]
+
+[[package]]
name = "psm"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1504,6 +3251,62 @@
]
[[package]]
+name = "quinn"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
+dependencies = [
+ "bytes",
+ "cfg_aliases",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash 2.1.2",
+ "rustls",
+ "socket2",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-proto"
+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",
+ "rand",
+ "ring",
+ "rustc-hash 2.1.2",
+ "rustls",
+ "rustls-pki-types",
+ "slab",
+ "thiserror",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
+dependencies = [
+ "cfg_aliases",
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1592,6 +3395,35 @@
]
[[package]]
+name = "redox_syscall"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
+dependencies = [
+ "getrandom 0.2.17",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
name = "regex"
version = "1.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1621,6 +3453,60 @@
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
+name = "reqwest"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0"
+dependencies = [
+ "base64",
+ "bytes",
+ "encoding_rs",
+ "futures-channel",
+ "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",
+ "sync_wrapper",
+ "tokio",
+ "tokio-rustls",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.17",
+ "libc",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
name = "rowan"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1645,6 +3531,15 @@
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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1654,10 +3549,85 @@
"errno",
"libc",
"linux-raw-sys",
- "windows-sys",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustls"
+version = "0.23.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
+dependencies = [
+ "aws-lc-rs",
+ "once_cell",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
+dependencies = [
+ "openssl-probe",
+ "rustls-pki-types",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
+dependencies = [
+ "web-time",
+ "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",
+]
+
+[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1673,6 +3643,44 @@
]
[[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 = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1740,13 +3748,34 @@
[[package]]
name = "sha1"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.2.17",
+ "digest 0.10.7",
+]
+
+[[package]]
+name = "sha1"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214"
dependencies = [
"cfg-if",
- "cpufeatures",
- "digest",
+ "cpufeatures 0.3.0",
+ "digest 0.11.3",
+]
+
+[[package]]
+name = "sha1-checked"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423"
+dependencies = [
+ "digest 0.10.7",
+ "sha1 0.10.6",
]
[[package]]
@@ -1756,8 +3785,8 @@
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
dependencies = [
"cfg-if",
- "cpufeatures",
- "digest",
+ "cpufeatures 0.3.0",
+ "digest 0.11.3",
]
[[package]]
@@ -1766,29 +3795,102 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1"
dependencies = [
- "digest",
+ "digest 0.11.3",
"keccak",
]
[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "shell-words"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
+
+[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
+name = "signal-hook"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2a0c28ca5908dbdbcd52e6fdaa00358ab88637f8ab33e1f188dd510eb44b53d"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
+dependencies = [
+ "errno",
+ "libc",
+]
+
+[[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.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 = "similar"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
[[package]]
+name = "slab"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+
+[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
+name = "socket2"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1804,7 +3906,7 @@
"cfg-if",
"libc",
"psm",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -1835,6 +3937,12 @@
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1857,6 +3965,15 @@
]
[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
name = "synstructure"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1877,7 +3994,7 @@
"getrandom 0.4.2",
"once_cell",
"rustix",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -1921,6 +4038,15 @@
]
[[package]]
+name = "thread_local"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
name = "tinystr"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1941,18 +4067,203 @@
]
[[package]]
+name = "tinyvec"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tokio"
+version = "1.52.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386"
+dependencies = [
+ "bytes",
+ "libc",
+ "mio",
+ "pin-project-lite",
+ "socket2",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tower"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28f0d049ccfaa566e14e9663d304d8577427b368cb4710a20528690287a738b"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "pin-project-lite",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "url",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex-automata",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "typed-path"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
+
+[[package]]
name = "typenum"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
[[package]]
+name = "uluru"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c8a2469e56e6e5095c82ccd3afb98dad95f7af7929aab6d8ba8d6e0f73657da"
+dependencies = [
+ "arrayvec",
+]
+
+[[package]]
name = "ungrammar"
version = "1.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e5df347f0bf3ec1d670aad6ca5c6a1859cd9ea61d2113125794654ccced68f"
[[package]]
+name = "unicode-bom"
+version = "2.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217"
+
+[[package]]
name = "unicode-box-drawing"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1965,6 +4276,15 @@
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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1977,6 +4297,12 @@
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
name = "url"
version = "2.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2001,6 +4327,12 @@
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
+[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2026,6 +4358,21 @@
]
[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
name = "wasip2"
version = "1.0.3+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2146,6 +4493,25 @@
]
[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki-root-certs"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2167,7 +4533,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -2184,6 +4550,24 @@
[[package]]
name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.5",
+]
+
+[[package]]
+name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
@@ -2192,6 +4576,135 @@
]
[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
+dependencies = [
+ "windows-link",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm 0.53.1",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+
+[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2392,6 +4905,12 @@
]
[[package]]
+name = "zeroize"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+
+[[package]]
name = "zerotrie"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2425,7 +4944,39 @@
]
[[package]]
+name = "zip"
+version = "8.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b"
+dependencies = [
+ "crc32fast",
+ "flate2",
+ "indexmap",
+ "memchr",
+ "typed-path",
+ "zopfli",
+]
+
+[[package]]
+name = "zlib-rs"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513"
+
+[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+
+[[package]]
+name = "zopfli"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
+dependencies = [
+ "bumpalo",
+ "crc32fast",
+ "log",
+ "simd-adler32",
+]
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,6 +30,7 @@
jrsonnet-types = { path = "./crates/jrsonnet-types", version = "0.5.0-pre98" }
jrsonnet-formatter = { path = "./crates/jrsonnet-formatter", version = "0.5.0-pre98" }
jrsonnet-lexer = { path = "./crates/jrsonnet-lexer", version = "0.5.0-pre98" }
+jrsonnet-pkg = { path = "./crates/jrsonnet-pkg", version = "0.5.0-pre98" }
jrsonnet-gcmodule = { version = "0.5.0" }
# Diagnostics.
# hi-doc is my library, which handles text formatting very well, but isn't polished enough yet
@@ -116,6 +117,21 @@
console_error_panic_hook = "0.1"
getrandom = "0.3.4"
+# Bundler
+tracing = "0.1.44"
+tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }
+reqwest = { version = "0.13", features = [
+ "blocking",
+ "rustls",
+], default-features = false }
+zip = { version = "8", default-features = false, features = ["deflate"] }
+directories = "6.0.0"
+gix = { version = "0.83.0", features = [
+ "blocking-network-client",
+ "blocking-http-transport-reqwest-rust-tls",
+] }
+camino = { version = "1.2.2", features = ["serde1"] }
+
[workspace.lints.rust]
unsafe_op_in_unsafe_fn = "deny"
cmds/jrb/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/cmds/jrb/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "jrb"
+description = "jsonnet package manager"
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+version.workspace = true
+
+[lints]
+workspace = true
+
+[dependencies]
+jrsonnet-pkg.workspace = true
+
+clap = { workspace = true, features = ["derive"] }
+serde = { workspace = true }
+serde_json.workspace = true
+tracing.workspace = true
+tracing-subscriber.workspace = true
cmds/jrb/src/main.rsdiffbeforeafterboth1use std::{2 path::{Path, PathBuf},3 process::exit,4};56use clap::{Parser, Subcommand};7use jrsonnet_pkg::{8 install,9 jsonnet_bundler::{GitSource, JsonnetFile},10};11use tracing::{error, info, warn};1213#[derive(Parser)]14#[clap(about = "A jsonnet package manager")]15struct Opts {16 /// The directory used to cache packages in.17 #[clap(long, default_value = "vendor")]18 jsonnetpkg_home: PathBuf,19 #[clap(subcommand)]20 command: Command,21}2223#[derive(Subcommand)]24enum Command {25 /// Initialize a new empty jsonnetfile26 Init,27 /// Install new dependencies. Existing ones are silently skipped28 Install {29 /// Package URIs to install30 uris: Vec<String>,31 /// Show what would be done without making changes32 #[clap(long)]33 dry_run: bool,34 },35 /// Update all or specific dependencies36 Update {37 /// Package URIs to update (all if empty)38 uris: Vec<String>,39 /// Show what would be done without making changes40 #[clap(long)]41 dry_run: bool,42 },43 /// Remove dependencies by name44 Remove {45 /// Dependency names (matched against both canonical and legacy names)46 names: Vec<String>,47 /// Show what would be removed without making changes48 #[clap(long)]49 dry_run: bool,50 },51}5253const MANIFEST: &str = "jsonnetfile.json";54const LOCKFILE: &str = "jsonnetfile.lock.json";5556fn load_manifest() -> JsonnetFile {57 let path = Path::new(MANIFEST);58 if path.exists() {59 JsonnetFile::load(path).unwrap_or_else(|e| {60 error!("failed to load {MANIFEST}: {e}");61 exit(1);62 })63 } else {64 JsonnetFile {65 version: 1,66 dependencies: Vec::new(),67 legacy_imports: true,68 }69 }70}7172fn save_json(path: &Path, value: &impl serde::Serialize) {73 let json = serde_json::to_string_pretty(value).expect("serialization failed");74 std::fs::write(path, format!("{json}\n")).unwrap_or_else(|e| {75 error!("failed to write {}: {e}", path.display());76 exit(1);77 });78}7980fn load_lockfile() -> Option<JsonnetFile> {81 let path = Path::new(LOCKFILE);82 if path.exists() {83 Some(JsonnetFile::load(path).unwrap_or_else(|e| {84 error!("failed to load {LOCKFILE}: {e}");85 exit(1);86 }))87 } else {88 None89 }90}9192fn do_install(93 manifest: &JsonnetFile,94 lock: Option<&JsonnetFile>,95 vendor_dir: &Path,96 dry_run: bool,97) {98 let new_lock = install::install(manifest, lock, vendor_dir, dry_run).unwrap_or_else(|e| {99 error!("install failed: {e}");100 exit(1);101 });102 if !dry_run {103 save_json(Path::new(LOCKFILE), &new_lock);104 }105}106107fn main() {108 tracing_subscriber::fmt().init();109110 let opts = Opts::parse();111112 match opts.command {113 Command::Init => {114 let path = Path::new(MANIFEST);115 if path.exists() {116 warn!("{MANIFEST} already exists");117 exit(1);118 }119 let jf = JsonnetFile {120 version: 1,121 dependencies: Vec::new(),122 legacy_imports: true,123 };124 save_json(path, &jf);125 }126 Command::Install { uris, dry_run } => {127 let mut manifest = load_manifest();128129 for uri in &uris {130 let dep = GitSource::parse(uri).unwrap_or_else(|| {131 eprintln!("failed to parse URI: {uri}");132 exit(1);133 });134 let is_new = !manifest.dependencies.iter().any(|d| {135 std::mem::discriminant(&d.source) == std::mem::discriminant(&dep.source)136 && d.canonical_name() == dep.canonical_name()137 });138 if is_new {139 manifest.dependencies.push(dep);140 }141 }142143 if !uris.is_empty() {144 save_json(Path::new(MANIFEST), &manifest);145 }146147 let lock = load_lockfile();148 do_install(&manifest, lock.as_ref(), &opts.jsonnetpkg_home, dry_run);149 }150 Command::Update { uris, dry_run } => {151 let mut manifest = load_manifest();152153 if !uris.is_empty() {154 for uri in &uris {155 let dep = GitSource::parse(uri).unwrap_or_else(|| {156 eprintln!("failed to parse URI: {uri}");157 exit(1);158 });159 if let Some(existing) = manifest160 .dependencies161 .iter_mut()162 .find(|d| d.canonical_name() == dep.canonical_name())163 {164 *existing = dep;165 } else {166 manifest.dependencies.push(dep);167 }168 }169 save_json(Path::new(MANIFEST), &manifest);170 }171172 do_install(&manifest, None, &opts.jsonnetpkg_home, dry_run);173 }174 Command::Remove { names, dry_run } => {175 let mut manifest = load_manifest();176177 let matched: Vec<_> = manifest178 .dependencies179 .iter()180 .filter(|dep| {181 names.iter().any(|name| {182 dep.canonical_name() == *name || dep.legacy_link_name() == *name183 })184 })185 .cloned()186 .collect::<Vec<_>>();187188 if matched.is_empty() {189 eprintln!("no matching dependencies found");190 exit(1);191 }192193 for dep in &matched {194 let canonical = dep.canonical_name();195 let dir = opts.jsonnetpkg_home.join(&canonical);196 let legacy = dep.legacy_link_name();197 let link = opts.jsonnetpkg_home.join(&legacy);198 if dry_run {199 info!("would remove: {canonical} ({})", dir.display());200 } else {201 info!("removing: {canonical}");202 if dir.exists() {203 let _ = std::fs::remove_dir_all(&dir);204 }205 if link.symlink_metadata().is_ok() {206 let _ = std::fs::remove_file(&link);207 }208 }209 }210211 if !dry_run {212 manifest.dependencies.retain(|dep| {213 !names.iter().any(|name| {214 dep.canonical_name() == *name || dep.legacy_link_name() == *name215 })216 });217 save_json(Path::new(MANIFEST), &manifest);218 save_json(Path::new(LOCKFILE), &manifest);219 }220 }221 }222}crates/jrsonnet-pkg/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-pkg/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+name = "jrsonnet-pkg"
+description = "jsonnet-bundler jsonnetfile parser and installer"
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+version.workspace = true
+
+[lints]
+workspace = true
+
+[dependencies]
+serde = { workspace = true, features = ["derive"] }
+serde_json.workspace = true
+thiserror.workspace = true
+tracing.workspace = true
+
+# Source url parser
+peg.workspace = true
+
+# Gix for git repos, reqwest + zip for github
+gix.workspace = true
+reqwest.workspace = true
+zip.workspace = true
+url.workspace = true
+camino.workspace = true
+
+# Global cache dir
+directories.workspace = true
crates/jrsonnet-pkg/src/install/accessor.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-pkg/src/install/accessor.rs
@@ -0,0 +1,132 @@
+use std::{
+ fs::File,
+ io::{self, Read},
+ result,
+ str::FromStr as _,
+ sync::Mutex,
+};
+
+use tracing::warn;
+use zip::{ZipArchive, result::ZipError};
+
+use crate::jsonnet_bundler::{SubDir, SubDirEscapeError};
+
+#[derive(thiserror::Error, Debug)]
+pub enum Error {
+ #[error(transparent)]
+ Zip(#[from] ZipError),
+ #[error("invalid prefixed archive")]
+ ZipInvalidPrefix,
+ #[error("zip io: {0}")]
+ ZipIo(io::Error),
+ #[error("subdir not found: {0}")]
+ SubDirNotFound(SubDir),
+ #[error(transparent)]
+ SubdirEscape(#[from] SubDirEscapeError),
+}
+type Result<T, E = Error> = result::Result<T, E>;
+
+pub trait SourceAccessor {}
+
+pub struct ZipFileAccessor {
+ archive: Mutex<ZipArchive<File>>,
+ // Github archives have top-level directory with repo name
+ prefix: SubDir,
+}
+
+impl ZipFileAccessor {
+ pub fn new_prefixed(file: File) -> Result<Self> {
+ let archive = ZipArchive::new(file)?;
+ let prefix = archive.name_for_index(0).ok_or(Error::ZipInvalidPrefix)?;
+
+ Ok(Self {
+ prefix: SubDir::from_str(prefix)?,
+ archive: Mutex::new(archive),
+ })
+ }
+ /// Read a file from inside the archive's logical root (after stripping the
+ /// github-style `<repo>-<sha>/` prefix).
+ #[allow(clippy::significant_drop_tightening, reason = "false-positive")]
+ pub fn read(&self, name: &SubDir) -> Result<Option<Vec<u8>>> {
+ let prefixed = self
+ .prefix
+ .join(name)
+ .expect("prefix and name are both subdirs");
+ let mut archive = self.archive.lock().expect("not poisoned");
+ let mut v = match archive.by_name(prefixed.as_str()) {
+ Ok(v) => v,
+ Err(ZipError::FileNotFound) => return Ok(None),
+ Err(e) => return Err(e.into()),
+ };
+ if !v.is_file() {
+ return Ok(None);
+ }
+ let mut out = Vec::new();
+ v.read_to_end(&mut out).map_err(Error::ZipIo)?;
+ Ok(Some(out))
+ }
+ #[allow(clippy::significant_drop_tightening, reason = "false-positive")]
+ pub fn iter<E>(
+ &self,
+ subdir: &SubDir,
+ cb: &mut dyn FnMut(SubDir, AccessorEntry) -> Result<(), E>,
+ ) -> Result<(), E>
+ where
+ E: From<Error>,
+ {
+ let mut archive = self.archive.lock().expect("not poisoned");
+ let len = archive.len();
+
+ let mut found = false;
+ for i in 0..len {
+ let mut entry = archive.by_index(i).map_err(Error::from)?;
+ let raw = entry.name();
+ let Ok(full_name) = SubDir::from_str(raw) else {
+ warn!("invalid zip entry name: {raw}");
+ continue;
+ };
+ // Peel off the github-archive top-level `<repo>-<sha>/` prefix.
+ let Some(in_repo) = full_name.strip_prefix(&self.prefix) else {
+ continue;
+ };
+ let Some(name) = in_repo.strip_prefix(subdir) else {
+ continue;
+ };
+ found = true;
+ if name.is_empty() && entry.is_dir() {
+ continue;
+ }
+
+ cb(
+ name.clone(),
+ if entry.is_dir() {
+ AccessorEntry::Dir
+ } else if entry.is_file() {
+ let mut data = Vec::new();
+ entry.read_to_end(&mut data).map_err(Error::ZipIo)?;
+ AccessorEntry::File(data)
+ } else {
+ // TODO: Symlinks?
+ panic!("unknown accessor entry type: {name:?}")
+ },
+ )?;
+ }
+
+ if !found {
+ return Err(Error::SubDirNotFound(subdir.clone()).into());
+ }
+
+ Ok(())
+ }
+ pub fn len(&self) -> usize {
+ self.archive.lock().expect("not poisoned").len()
+ }
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+}
+
+pub enum AccessorEntry {
+ Dir,
+ File(Vec<u8>),
+}
crates/jrsonnet-pkg/src/install/git.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-pkg/src/install/git.rs
@@ -0,0 +1,212 @@
+#![allow(clippy::result_large_err)]
+
+use std::{collections::HashSet, fs, path::Path};
+
+use gix::{
+ bstr::{self, ByteSlice},
+ interrupt, progress,
+ remote::{self, ref_map},
+};
+use tracing::info;
+
+use super::{Error, LocalExtraction, ResolveResult, Result, VendorSource, cache_dir};
+use crate::jsonnet_bundler::{Dependency, GitSource, JsonnetFile, Source, SubDir};
+
+fn repo_cache_path(remote: &GitSource) -> Result<std::path::PathBuf> {
+ Ok(cache_dir("git")?.join(&remote.host).join(&remote.repo))
+}
+
+fn ensure_repo(remote: &GitSource) -> Result<gix::Repository> {
+ let cache_path = repo_cache_path(remote)?;
+
+ if cache_path.exists() {
+ if let Ok(repo) = gix::open(&cache_path) {
+ fetch_remote(&repo, &remote.remote())?;
+ return Ok(repo);
+ }
+ fs::remove_dir_all(&cache_path).map_err(|e| Error::Io(cache_path.clone(), e))?;
+ }
+
+ fs::create_dir_all(cache_path.parent().expect("has parent"))
+ .map_err(|e| Error::Io(cache_path.clone(), e))?;
+
+ let mut clone = gix::prepare_clone_bare(remote.remote(), &cache_path)?;
+ let (repo, _) = clone.fetch_only(progress::Discard, &interrupt::IS_INTERRUPTED)?;
+ fetch_remote(&repo, &remote.remote())?;
+
+ Ok(repo)
+}
+
+fn fetch_remote(repo: &gix::Repository, remote: &str) -> Result<(), Error> {
+ repo.remote_at(remote)?
+ .with_refspecs(["+refs/*:refs/*"], remote::Direction::Fetch)?
+ .connect(remote::Direction::Fetch)?
+ .prepare_fetch(progress::Discard, ref_map::Options::default())?
+ .receive(progress::Discard, &interrupt::IS_INTERRUPTED)?;
+ Ok(())
+}
+
+fn extract_tree(
+ repo: &gix::Repository,
+ tree: &gix::Tree<'_>,
+ subdir: &SubDir,
+ dest: &Path,
+) -> Result<(), Error> {
+ let target_tree;
+ let tree = if subdir.is_empty() {
+ tree
+ } else {
+ let mut t = tree.clone();
+ let entry = t
+ .peel_to_entry_by_path(subdir.as_path().as_std_path())?
+ .ok_or_else(|| Error::SubdirNotFound(subdir.to_string()))?;
+ target_tree = entry.object()?.into_tree();
+ &target_tree
+ };
+
+ let files = tree.traverse().breadthfirst.files()?;
+
+ for entry in &files {
+ if !entry.mode.is_blob() {
+ continue;
+ }
+ let rel_path = entry
+ .filepath
+ .to_str()
+ .map_err(|_| Error::InvalidPath(entry.filepath.to_string()))?;
+ let file_path = dest.join(rel_path);
+
+ if let Some(parent) = file_path.parent() {
+ fs::create_dir_all(parent).map_err(|e| Error::Io(parent.to_owned(), e))?;
+ }
+
+ let blob = repo.find_object(entry.oid)?;
+ fs::write(&file_path, &blob.data).map_err(|e| Error::Io(file_path, e))?;
+ }
+
+ Ok(())
+}
+
+fn resolve_version<'r>(repo: &'r gix::Repository, version: &str) -> Result<gix::Id<'r>> {
+ let spec: &bstr::BStr = version.into();
+ if let Ok(id) = repo.rev_parse_single(spec) {
+ return Ok(id);
+ }
+ for prefix in ["refs/heads/", "refs/tags/"] {
+ let refname = format!("{prefix}{version}");
+ if let Ok(r) = repo.find_reference(&refname) {
+ return Ok(r.into_fully_peeled_id()?);
+ }
+ }
+ Ok(repo.rev_parse_single(spec)?)
+}
+
+fn read_blob_at_path(
+ repo: &gix::Repository,
+ tree: &gix::Tree<'_>,
+ path: &SubDir,
+) -> Option<Vec<u8>> {
+ let mut t = tree.clone();
+ let entry = t
+ .peel_to_entry_by_path(path.as_path().as_std_path())
+ .ok()??;
+ let blob = repo.find_object(entry.oid()).ok()?;
+ Some(blob.data.clone())
+}
+
+fn collect_tree_deps(
+ repo: &gix::Repository,
+ tree: &gix::Tree<'_>,
+ dir: &SubDir,
+ git_deps: &mut Vec<Dependency>,
+ local_extractions: &mut Vec<LocalExtraction>,
+ visited: &mut HashSet<SubDir>,
+) {
+ if !visited.insert(dir.clone()) {
+ return;
+ }
+
+ let manifest_path = dir
+ .join("jsonnetfile.json")
+ .expect("appending a literal filename keeps it within parent");
+ let Some(data) = read_blob_at_path(repo, tree, &manifest_path) else {
+ return;
+ };
+ let Ok(manifest) = serde_json::from_slice::<JsonnetFile>(&data) else {
+ return;
+ };
+
+ for dep in manifest.dependencies {
+ match &dep.source {
+ Source::Git(_) => git_deps.push(dep),
+ Source::Local(local) => {
+ let Ok(child_dir) = local.resolve_under(dir) else {
+ info!("local source {local} escapes its package; skipping");
+ continue;
+ };
+ let name = child_dir
+ .file_name()
+ .map_or_else(|| local.to_string(), str::to_owned);
+ local_extractions.push(LocalExtraction {
+ tree_path: child_dir.clone(),
+ name,
+ });
+ collect_tree_deps(repo, tree, &child_dir, git_deps, local_extractions, visited);
+ }
+ }
+ }
+}
+
+pub(super) fn resolve(
+ git_source: &GitSource,
+ version: Option<&str>,
+) -> Result<ResolveResult, Error> {
+ info!("fetching via git: {}", git_source.remote());
+ let repo = ensure_repo(git_source)?;
+ let id = match version {
+ Some(v) => resolve_version(&repo, v)?,
+ None => repo.head_id()?,
+ };
+ let commit = repo.find_object(id)?.into_commit();
+ let tree = commit.tree()?;
+
+ let mut transitive_git_deps = Vec::new();
+ let mut local_extractions = Vec::new();
+ let mut visited = HashSet::new();
+ collect_tree_deps(
+ &repo,
+ &tree,
+ &git_source.subdir,
+ &mut transitive_git_deps,
+ &mut local_extractions,
+ &mut visited,
+ );
+
+ let repo_path = repo_cache_path(git_source)?;
+ let sha = id.to_string();
+
+ Ok(ResolveResult {
+ version: sha.clone(),
+ transitive_git_deps,
+ local_extractions,
+ source: VendorSource::GitTree {
+ repo_path,
+ commit_sha: sha,
+ subdir: git_source.subdir.clone(),
+ },
+ })
+}
+
+pub(super) fn extract(
+ repo_path: &Path,
+ commit_sha: &str,
+ subdir: &SubDir,
+ dest: &Path,
+) -> Result<(), Error> {
+ let repo = gix::open(repo_path)?;
+ let spec: &bstr::BStr = commit_sha.into();
+ let id = repo.rev_parse_single(spec)?;
+ let commit = repo.find_object(id)?.into_commit();
+ let tree = commit.tree()?;
+ extract_tree(&repo, &tree, subdir, dest)
+}
crates/jrsonnet-pkg/src/install/github.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-pkg/src/install/github.rs
@@ -0,0 +1,190 @@
+#![allow(clippy::result_large_err)]
+
+use std::{
+ collections::HashSet,
+ fs::{self, File},
+ io::Write as _,
+ path::{Path, PathBuf},
+};
+
+use reqwest::{blocking::Response, header};
+use tracing::{debug, info};
+
+use super::{
+ Error, LocalExtraction, ResolveResult, Result, VendorSource,
+ accessor::{AccessorEntry, ZipFileAccessor},
+};
+use crate::{
+ install::{PKG_USER_AGENT, cache_dir},
+ jsonnet_bundler::{Dependency, GitSource, JsonnetFile, Source, SubDir},
+};
+
+fn is_sha(s: &str) -> bool {
+ s.len() == 40 && s.bytes().all(|b| b.is_ascii_hexdigit())
+}
+
+fn commit_cache_path(source: &GitSource, sha: &str) -> Result<PathBuf> {
+ Ok(cache_dir("github")?
+ .join(source.plain_repo_name())
+ .join(format!("{sha}.zip")))
+}
+
+fn resolve_sha(source: &GitSource, version: &str) -> Result<String> {
+ let url = format!(
+ "https://api.github.com/repos/{}/commits/{}",
+ source.plain_repo_name(),
+ version
+ );
+ let response = reqwest::blocking::Client::new()
+ .get(&url)
+ .header(header::ACCEPT, "application/vnd.github.sha")
+ .header(header::USER_AGENT, PKG_USER_AGENT)
+ .send()
+ .and_then(Response::error_for_status)?;
+ let sha = response.text()?;
+ Ok(sha.trim().to_owned())
+}
+
+fn fetch_zip(source: &GitSource, sha: &str) -> Result<ZipFileAccessor> {
+ let cached = commit_cache_path(source, sha)?;
+ if cached.exists() {
+ debug!("using cached archive {}", cached.display());
+ return Ok(ZipFileAccessor::new_prefixed(
+ File::open(&cached).map_err(|e| Error::Io(cached.clone(), e))?,
+ )?);
+ }
+
+ let url = format!(
+ "https://github.com/{}/archive/{}.zip",
+ source.plain_repo_name(),
+ sha
+ );
+ info!("downloading {url}");
+
+ let bytes = reqwest::blocking::Client::new()
+ .get(&url)
+ .header(header::USER_AGENT, PKG_USER_AGENT)
+ .send()
+ .and_then(Response::error_for_status)?
+ .bytes()?;
+
+ if let Some(parent) = cached.parent() {
+ fs::create_dir_all(parent).map_err(|e| Error::Io(parent.to_owned(), e))?;
+ }
+ let mut downloaded = File::create_new(&cached).map_err(|e| Error::Io(cached.clone(), e))?;
+ downloaded
+ .write_all(&bytes)
+ .map_err(|e| Error::Io(cached.clone(), e))?;
+
+ Ok(ZipFileAccessor::new_prefixed(downloaded)?)
+}
+
+fn open_cached_zip(zip_path: &Path) -> Result<ZipFileAccessor> {
+ Ok(ZipFileAccessor::new_prefixed(
+ File::open(zip_path).map_err(|e| Error::Io(zip_path.to_owned(), e))?,
+ )?)
+}
+
+fn extract_subdir(archive: &ZipFileAccessor, subdir: &SubDir, dest: &Path) -> Result<()> {
+ archive.iter(subdir, &mut |name, entry| {
+ let target = dest.join(name);
+ match entry {
+ AccessorEntry::Dir => {
+ fs::create_dir_all(&target).map_err(|e| Error::Io(target, e))?;
+ }
+ AccessorEntry::File(data) => {
+ if let Some(parent) = target.parent() {
+ fs::create_dir_all(parent).map_err(|e| Error::Io(parent.to_owned(), e))?;
+ }
+ fs::write(&target, &data).map_err(|e| Error::Io(target, e))?;
+ }
+ }
+ Ok(())
+ })
+}
+
+fn collect_archive_deps(
+ archive: &ZipFileAccessor,
+ dir: &SubDir,
+ git_deps: &mut Vec<Dependency>,
+ local_extractions: &mut Vec<LocalExtraction>,
+ visited: &mut HashSet<SubDir>,
+) -> Result<()> {
+ if !visited.insert(dir.clone()) {
+ return Ok(());
+ }
+
+ let manifest_path = dir
+ .join("jsonnetfile.json")
+ .expect("appending a literal filename keeps it within parent");
+
+ let Some(data) = archive.read(&manifest_path)? else {
+ return Ok(());
+ };
+ let Ok(manifest) = serde_json::from_slice::<JsonnetFile>(&data) else {
+ return Ok(());
+ };
+
+ for dep in manifest.dependencies {
+ match &dep.source {
+ Source::Git(_) => git_deps.push(dep),
+ Source::Local(local) => {
+ let Ok(child_dir) = local.resolve_under(dir) else {
+ tracing::info!("local source {local} escapes its package; skipping");
+ continue;
+ };
+ let name = child_dir
+ .file_name()
+ .map_or_else(|| local.to_string(), str::to_owned);
+ local_extractions.push(LocalExtraction {
+ tree_path: child_dir.clone(),
+ name,
+ });
+ collect_archive_deps(archive, &child_dir, git_deps, local_extractions, visited)?;
+ }
+ }
+ }
+ Ok(())
+}
+
+pub(super) fn resolve(source: &GitSource, version: Option<&str>) -> Result<ResolveResult> {
+ let version_str = version.unwrap_or("HEAD");
+ let sha = if is_sha(version_str) {
+ version_str.to_owned()
+ } else {
+ let resolved = resolve_sha(source, version_str)?;
+ info!("resolved {version_str} to {resolved}");
+ resolved
+ };
+
+ let archive = fetch_zip(source, &sha)?;
+
+ let mut transitive_git_deps = Vec::new();
+ let mut local_extractions = Vec::new();
+ let mut visited = HashSet::new();
+ collect_archive_deps(
+ &archive,
+ &source.subdir,
+ &mut transitive_git_deps,
+ &mut local_extractions,
+ &mut visited,
+ )?;
+
+ let zip_path = commit_cache_path(source, &sha)?;
+
+ Ok(ResolveResult {
+ version: sha.clone(),
+ transitive_git_deps,
+ local_extractions,
+ source: VendorSource::GithubZip {
+ zip_path,
+ commit_sha: sha,
+ subdir: source.subdir.clone(),
+ },
+ })
+}
+
+pub(super) fn extract(zip_path: &Path, subdir: &SubDir, dest: &Path) -> Result<()> {
+ let archive = open_cached_zip(zip_path)?;
+ extract_subdir(&archive, subdir, dest)
+}
crates/jrsonnet-pkg/src/install/mod.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-pkg/src/install/mod.rs
@@ -0,0 +1,406 @@
+#![allow(clippy::result_large_err)]
+
+pub mod accessor;
+mod git;
+mod github;
+
+use std::{
+ collections::{BTreeMap, HashSet},
+ fs,
+ path::{Path, PathBuf},
+ result,
+};
+
+use camino::Utf8PathBuf;
+use tracing::info;
+
+use crate::jsonnet_bundler::{Dependency, GitScheme, GitSource, JsonnetFile, Source, SubDir};
+
+pub const PKG_USER_AGENT: &str = "jrsonnet-pkg (https://delta.rocks/jrsonnet)";
+
+pub fn cache_dir(subdir: &str) -> Result<std::path::PathBuf> {
+ Ok(directories::ProjectDirs::from("rocks", "delta", "jrsonnet")
+ .ok_or(Error::XdgUnavailable)?
+ .cache_dir()
+ .join(subdir))
+}
+
+pub(crate) struct LocalExtraction {
+ /// Path inside the parent repo's tree where this local source lives.
+ pub tree_path: SubDir,
+ pub name: String,
+}
+
+pub(crate) struct ResolveResult {
+ pub version: String,
+ pub transitive_git_deps: Vec<Dependency>,
+ pub local_extractions: Vec<LocalExtraction>,
+ pub source: VendorSource,
+}
+
+const VERSION_FILE: &str = ".version";
+
+/// How to populate a vendor path.
+pub enum VendorSource {
+ GitTree {
+ repo_path: PathBuf,
+ commit_sha: String,
+ subdir: SubDir,
+ },
+ GithubZip {
+ zip_path: PathBuf,
+ commit_sha: String,
+ subdir: SubDir,
+ },
+ Symlink(Utf8PathBuf),
+}
+
+impl VendorSource {
+ fn with_subdir(&self, new_subdir: SubDir) -> Self {
+ match self {
+ VendorSource::GitTree {
+ repo_path,
+ commit_sha,
+ ..
+ } => VendorSource::GitTree {
+ repo_path: repo_path.clone(),
+ commit_sha: commit_sha.clone(),
+ subdir: new_subdir,
+ },
+ VendorSource::GithubZip {
+ zip_path,
+ commit_sha,
+ ..
+ } => VendorSource::GithubZip {
+ zip_path: zip_path.clone(),
+ commit_sha: commit_sha.clone(),
+ subdir: new_subdir,
+ },
+ VendorSource::Symlink(target) => VendorSource::Symlink(target.clone()),
+ }
+ }
+}
+
+pub struct InstallPlan {
+ pub lock: JsonnetFile,
+ /// vendor-relative path -> how to obtain it.
+ pub entries: BTreeMap<Utf8PathBuf, VendorSource>,
+}
+
+pub fn install(
+ manifest: &JsonnetFile,
+ lock: Option<&JsonnetFile>,
+ vendor_dir: &Path,
+ dry_run: bool,
+) -> Result<JsonnetFile, Error> {
+ let plan = resolve(manifest, lock)?;
+ execute(&plan, vendor_dir, dry_run)?;
+ Ok(plan.lock)
+}
+
+pub fn resolve(manifest: &JsonnetFile, lock: Option<&JsonnetFile>) -> Result<InstallPlan, Error> {
+ let mut plan = InstallPlan {
+ lock: JsonnetFile {
+ version: manifest.version,
+ dependencies: Vec::new(),
+ legacy_imports: manifest.legacy_imports,
+ },
+ entries: BTreeMap::new(),
+ };
+ let mut installed = HashSet::new();
+
+ resolve_deps(
+ &manifest.dependencies,
+ lock,
+ manifest.legacy_imports,
+ &mut plan,
+ &mut installed,
+ )?;
+
+ Ok(plan)
+}
+
+fn is_up_to_date(dest: &Path, version: &str) -> bool {
+ fs::read_to_string(dest.join(VERSION_FILE)).is_ok_and(|v| v.trim() == version)
+}
+
+fn write_version(dest: &Path, version: &str) -> Result<(), Error> {
+ fs::write(dest.join(VERSION_FILE), format!("{version}\n"))
+ .map_err(|e| Error::Io(dest.join(VERSION_FILE), e))
+}
+
+pub fn execute(plan: &InstallPlan, vendor_dir: &Path, dry_run: bool) -> Result<(), Error> {
+ if !dry_run {
+ for (path, source) in &plan.entries {
+ let dest = vendor_dir.join(path);
+ match source {
+ VendorSource::GitTree {
+ repo_path,
+ commit_sha,
+ subdir,
+ } => {
+ if is_up_to_date(&dest, commit_sha) {
+ continue;
+ }
+ info!("extract {path}");
+ if dest.exists() {
+ fs::remove_dir_all(&dest).map_err(|e| Error::Io(dest.clone(), e))?;
+ }
+ fs::create_dir_all(&dest).map_err(|e| Error::Io(dest.clone(), e))?;
+ git::extract(repo_path, commit_sha, subdir, &dest)?;
+ write_version(&dest, commit_sha)?;
+ }
+ VendorSource::GithubZip {
+ zip_path,
+ commit_sha,
+ subdir,
+ } => {
+ if is_up_to_date(&dest, commit_sha) {
+ continue;
+ }
+ info!("extract {path}");
+ if dest.exists() {
+ fs::remove_dir_all(&dest).map_err(|e| Error::Io(dest.clone(), e))?;
+ }
+ fs::create_dir_all(&dest).map_err(|e| Error::Io(dest.clone(), e))?;
+ github::extract(zip_path, subdir, &dest)?;
+ write_version(&dest, commit_sha)?;
+ }
+ VendorSource::Symlink(_) => {}
+ }
+ }
+ for (path, source) in &plan.entries {
+ if let VendorSource::Symlink(target) = source {
+ let dest = vendor_dir.join(path);
+ if dest
+ .symlink_metadata()
+ .is_ok_and(|m| m.file_type().is_symlink())
+ {
+ if fs::read_link(&dest).is_ok_and(|t| t == target.as_std_path()) {
+ continue;
+ }
+ fs::remove_file(&dest).map_err(|e| Error::Io(dest.clone(), e))?;
+ }
+ info!("symlink {path} -> {target}");
+ std::os::unix::fs::symlink(target.as_std_path(), &dest)
+ .map_err(|e| Error::Io(dest.clone(), e))?;
+ }
+ }
+ }
+ prune(plan, vendor_dir, dry_run)?;
+ Ok(())
+}
+
+fn prune(plan: &InstallPlan, vendor_dir: &Path, dry_run: bool) -> Result<(), Error> {
+ if !vendor_dir.is_dir() {
+ return Ok(());
+ }
+ prune_recursive(plan, vendor_dir, vendor_dir, dry_run)
+}
+
+fn prune_recursive(
+ plan: &InstallPlan,
+ vendor_dir: &Path,
+ dir: &Path,
+ dry_run: bool,
+) -> Result<(), Error> {
+ let entries = fs::read_dir(dir).map_err(|e| Error::Io(dir.to_owned(), e))?;
+ for entry in entries {
+ let entry = entry.map_err(|e| Error::Io(dir.to_owned(), e))?;
+ let path = entry.path();
+ let rel = path
+ .strip_prefix(vendor_dir)
+ .expect("path is under vendor_dir");
+ let Ok(rel) = Utf8PathBuf::try_from(rel.to_owned()) else {
+ info!("prune (non-utf8) {}", rel.display());
+ continue;
+ };
+
+ if plan.entries.contains_key(&rel) {
+ continue;
+ }
+
+ let ft = entry.file_type().map_err(|e| Error::Io(path.clone(), e))?;
+ if ft.is_symlink() {
+ info!("prune {rel}");
+ if !dry_run {
+ fs::remove_file(&path).map_err(|e| Error::Io(path, e))?;
+ }
+ } else if ft.is_dir() {
+ let prefix: Utf8PathBuf = format!("{rel}/").into();
+ let has_descendants = plan
+ .entries
+ .range(prefix.clone()..)
+ .next()
+ .is_some_and(|(k, _)| k.starts_with(&prefix));
+ if has_descendants {
+ prune_recursive(plan, vendor_dir, &path, dry_run)?;
+ } else {
+ info!("prune {rel}");
+ if !dry_run {
+ fs::remove_dir_all(&path).map_err(|e| Error::Io(path, e))?;
+ }
+ }
+ } else {
+ info!("prune {rel}");
+ if !dry_run {
+ fs::remove_file(&path).map_err(|e| Error::Io(path, e))?;
+ }
+ }
+ }
+
+ if !dry_run
+ && dir != vendor_dir
+ && let Ok(mut entries) = fs::read_dir(dir)
+ && entries.next().is_none()
+ {
+ let _ = fs::remove_dir(dir);
+ }
+
+ Ok(())
+}
+
+fn resolve_one(git_source: &GitSource, version: Option<&str>) -> Result<ResolveResult, Error> {
+ if git_source.host == "github.com" && git_source.scheme == GitScheme::Https {
+ match github::resolve(git_source, version) {
+ Ok(result) => return Ok(result),
+ Err(e) => {
+ info!("github archive failed ({e}), falling back to git");
+ }
+ }
+ }
+ git::resolve(git_source, version)
+}
+
+fn locked_version<'a>(dep: &Dependency, lock: Option<&'a JsonnetFile>) -> Option<&'a str> {
+ let lock = lock?;
+ let key = dep.canonical_name();
+ lock.dependencies
+ .iter()
+ .find(|d| d.canonical_name() == key)
+ .and_then(|d| d.version.as_deref())
+}
+
+fn resolve_deps(
+ deps: &[Dependency],
+ lock: Option<&JsonnetFile>,
+ legacy_imports: bool,
+ plan: &mut InstallPlan,
+ installed: &mut HashSet<Utf8PathBuf>,
+) -> Result<(), Error> {
+ for dep in deps {
+ let Source::Git(git_source) = &dep.source else {
+ continue;
+ };
+
+ let canonical = dep.canonical_name();
+ if !installed.insert(canonical.clone()) {
+ continue;
+ }
+
+ let version = locked_version(dep, lock).or(dep.version.as_deref());
+
+ info!(
+ "resolving {canonical} (version: {})",
+ version.unwrap_or("<TBD>")
+ );
+
+ let result = resolve_one(git_source, version)?;
+
+ plan.lock.dependencies.push(Dependency {
+ source: dep.source.clone(),
+ version: Some(result.version),
+ sum: dep.sum.clone(),
+ name: dep.name.clone(),
+ single: dep.single,
+ });
+
+ let mut repo_base = Utf8PathBuf::from(git_source.host.as_str());
+ repo_base.push(git_source.plain_repo_name());
+
+ // Legacy symlink for the dep. Skipped if `legacyImports: false`, unless
+ // the user explicitly set `dep.name` (which is always honored).
+ if legacy_imports || dep.name.is_some() {
+ let legacy = Utf8PathBuf::from(dep.legacy_link_name());
+ if legacy != canonical {
+ plan.entries
+ .insert(legacy, VendorSource::Symlink(canonical.clone()));
+ }
+ }
+
+ for extraction in &result.local_extractions {
+ let extraction_canonical = repo_base.join(&extraction.tree_path);
+ plan.entries.insert(
+ extraction_canonical.clone(),
+ result.source.with_subdir(extraction.tree_path.clone()),
+ );
+ if legacy_imports {
+ let extraction_name = Utf8PathBuf::from(&extraction.name);
+ if extraction_name != extraction_canonical {
+ plan.entries
+ .insert(extraction_name, VendorSource::Symlink(extraction_canonical));
+ }
+ }
+ }
+
+ // Main entry (after local extractions used with_subdir)
+ plan.entries.insert(canonical, result.source);
+
+ resolve_deps(
+ &result.transitive_git_deps,
+ lock,
+ legacy_imports,
+ plan,
+ installed,
+ )?;
+ }
+
+ Ok(())
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+ #[error("io error for {0}: {1}")]
+ Io(PathBuf, std::io::Error),
+ #[error("failed to discover xdg directories")]
+ XdgUnavailable,
+ #[error("git clone failed: {0}")]
+ GitClone(#[from] gix::clone::Error),
+ #[error(transparent)]
+ GitRemote(#[from] gix::remote::init::Error),
+ #[error(transparent)]
+ GitConnect(#[from] gix::remote::connect::Error),
+ #[error(transparent)]
+ GitFetchPrepare(#[from] gix::remote::fetch::prepare::Error),
+ #[error(transparent)]
+ GitRemoteFetch(#[from] gix::remote::fetch::Error),
+ #[error(transparent)]
+ GitCloneFetch(#[from] gix::clone::fetch::Error),
+ #[error(transparent)]
+ GitFindObject(#[from] gix::object::find::existing::Error),
+ #[error(transparent)]
+ GitTraverse(#[from] gix::traverse::tree::breadthfirst::Error),
+ #[error(transparent)]
+ GitHead(#[from] gix::reference::head_id::Error),
+ #[error(transparent)]
+ GitCommit(#[from] gix::object::commit::Error),
+ #[error(transparent)]
+ GitRevparse(#[from] gix::revision::spec::parse::single::Error),
+ #[error(transparent)]
+ GitRefspec(#[from] gix::refspec::parse::Error),
+ #[error(transparent)]
+ GitPeel(#[from] gix::reference::peel::Error),
+ #[error(transparent)]
+ GitOpen(#[from] gix::open::Error),
+ #[error("http error: {0}")]
+ Http(#[from] reqwest::Error),
+ #[error("zip error: {0}")]
+ Zip(Box<zip::result::ZipError>),
+ #[error(transparent)]
+ Accessor(#[from] accessor::Error),
+ #[error("unknown subdir: {0}")]
+ SubdirNotFound(String),
+ #[error("invalid path in tree: {0}")]
+ InvalidPath(String),
+}
+pub(crate) type Result<T, E = Error> = result::Result<T, E>;
crates/jrsonnet-pkg/src/jsonnet_bundler.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-pkg/src/jsonnet_bundler.rs
@@ -0,0 +1,883 @@
+use std::{fmt, path::Path, str::FromStr};
+
+use camino::{Utf8Component, Utf8Path, Utf8PathBuf};
+use serde::{Deserialize, Serialize, de};
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct JsonnetFile {
+ pub version: u32,
+ #[serde(default)]
+ pub dependencies: Vec<Dependency>,
+ #[serde(default = "legacy_imports_default", rename = "legacyImports")]
+ pub legacy_imports: bool,
+}
+
+fn legacy_imports_default() -> bool {
+ true
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct Dependency {
+ pub source: Source,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub version: Option<String>,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub sum: Option<String>,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub name: Option<String>,
+ #[serde(default, skip_serializing_if = "is_false")]
+ pub single: bool,
+}
+
+#[allow(clippy::trivially_copy_pass_by_ref, reason = "serde")]
+fn is_false(v: &bool) -> bool {
+ !v
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "lowercase")]
+pub enum Source {
+ Git(GitSource),
+ Local(LocalSource),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum GitScheme {
+ Https,
+ Ssh,
+}
+
+/// Wrapper over `Utf8PathBuf`, ensuring it can't escape to either an absolute
+/// path or a parent directory.
+#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct SubDir(Utf8PathBuf);
+
+#[derive(Debug, thiserror::Error)]
+#[error("subdir attempted to escape")]
+pub struct SubDirEscapeError;
+
+impl FromStr for SubDir {
+ type Err = SubDirEscapeError;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Self::try_from(Utf8PathBuf::from(s))
+ }
+}
+impl TryFrom<Utf8PathBuf> for SubDir {
+ type Error = SubDirEscapeError;
+
+ fn try_from(buf: Utf8PathBuf) -> Result<Self, Self::Error> {
+ for ele in buf.components() {
+ match ele {
+ Utf8Component::Prefix(_) | Utf8Component::RootDir | Utf8Component::ParentDir => {
+ return Err(SubDirEscapeError);
+ }
+ Utf8Component::CurDir | Utf8Component::Normal(_) => {}
+ }
+ }
+ Ok(Self(buf))
+ }
+}
+
+impl SubDir {
+ pub fn empty() -> Self {
+ Self(Utf8PathBuf::new())
+ }
+ pub fn as_str(&self) -> &str {
+ self.0.as_str()
+ }
+ pub fn as_path(&self) -> &Utf8Path {
+ &self.0
+ }
+ pub fn into_inner(self) -> Utf8PathBuf {
+ self.0
+ }
+ pub fn join(&self, other: impl AsRef<Utf8Path>) -> Result<SubDir, SubDirEscapeError> {
+ SubDir::try_from(self.0.join(other))
+ }
+ pub fn strip_prefix(&self, prefix: &SubDir) -> Option<SubDir> {
+ Some(
+ SubDir::try_from(self.0.strip_prefix(&prefix.0).ok()?.to_owned())
+ .expect("stripping would not result in escape"),
+ )
+ }
+ pub fn is_empty(&self) -> bool {
+ self.0.as_str().is_empty()
+ }
+ pub fn file_name(&self) -> Option<&str> {
+ self.0.file_name()
+ }
+ /// Strip a trailing `.git` extension, if any.
+ #[must_use]
+ pub fn without_git_suffix(&self) -> SubDir {
+ let mut p = self.0.clone();
+ if p.extension() == Some("git") {
+ p.set_extension("");
+ }
+ SubDir(p)
+ }
+}
+impl AsRef<Utf8Path> for SubDir {
+ fn as_ref(&self) -> &Utf8Path {
+ &self.0
+ }
+}
+impl AsRef<Path> for SubDir {
+ fn as_ref(&self) -> &Path {
+ self.0.as_ref()
+ }
+}
+impl AsRef<str> for SubDir {
+ fn as_ref(&self) -> &str {
+ self.0.as_str()
+ }
+}
+impl fmt::Display for SubDir {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.0)
+ }
+}
+impl PartialEq<str> for SubDir {
+ fn eq(&self, other: &str) -> bool {
+ self.0.as_str() == other
+ }
+}
+impl PartialEq<&str> for SubDir {
+ fn eq(&self, other: &&str) -> bool {
+ self.0.as_str() == *other
+ }
+}
+
+/// Wrapper over `String`, guaranteeing the value is a valid host: only ASCII
+/// alphanumerics, dashes and dots, with at least one segment.
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct Hostname(String);
+
+#[derive(Debug, thiserror::Error)]
+#[error("invalid hostname")]
+pub struct InvalidHostnameError;
+
+impl FromStr for Hostname {
+ type Err = InvalidHostnameError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ if s.is_empty() || s == "." || s == ".." {
+ return Err(InvalidHostnameError);
+ }
+ for seg in s.split('.') {
+ if seg.is_empty() {
+ return Err(InvalidHostnameError);
+ }
+ if !seg.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'-') {
+ return Err(InvalidHostnameError);
+ }
+ }
+ Ok(Self(s.to_owned()))
+ }
+}
+
+impl Hostname {
+ pub fn as_str(&self) -> &str {
+ &self.0
+ }
+}
+impl AsRef<str> for Hostname {
+ fn as_ref(&self) -> &str {
+ &self.0
+ }
+}
+impl AsRef<Path> for Hostname {
+ fn as_ref(&self) -> &Path {
+ self.0.as_ref()
+ }
+}
+impl AsRef<Utf8Path> for Hostname {
+ fn as_ref(&self) -> &Utf8Path {
+ self.0.as_str().into()
+ }
+}
+impl fmt::Display for Hostname {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(&self.0)
+ }
+}
+impl PartialEq<str> for Hostname {
+ fn eq(&self, other: &str) -> bool {
+ self.0 == other
+ }
+}
+impl PartialEq<&str> for Hostname {
+ fn eq(&self, other: &&str) -> bool {
+ self.0 == *other
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct GitSource {
+ pub scheme: GitScheme,
+ pub host: Hostname,
+ /// Repo path relative to host: `user/repo[.git]` (or with subgroups).
+ pub repo: SubDir,
+ /// Subdirectory within the repo. Empty means the repo root.
+ pub subdir: SubDir,
+}
+
+/// A relative path that may climb out of its package via `..` parts, but only
+/// at the head - once you go down (`SubDir` portion) you can't go back up.
+///
+/// The total upward count is bounded only at resolution time, against the
+/// containing package's depth.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct LocalSource {
+ pub ups: usize,
+ pub dir: SubDir,
+}
+
+impl FromStr for LocalSource {
+ // Technically incorrect, as it only rejects mid-path ../'s...
+ type Err = SubDirEscapeError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let mut ups = 0usize;
+ let mut rest = s;
+ loop {
+ if let Some(r) = rest.strip_prefix("./") {
+ rest = r;
+ } else if rest == "." {
+ rest = "";
+ break;
+ } else if let Some(r) = rest.strip_prefix("../") {
+ ups = ups.checked_add(1).expect("can't be longer than s length");
+ rest = r;
+ } else if rest == ".." {
+ ups = ups.checked_add(1).expect("can't be longer than s length");
+ rest = "";
+ break;
+ } else {
+ break;
+ }
+ }
+ Ok(Self {
+ ups,
+ dir: SubDir::from_str(rest)?,
+ })
+ }
+}
+
+impl fmt::Display for LocalSource {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut out = String::with_capacity(self.ups * 3 + self.dir.as_str().len());
+ for _ in 0..self.ups {
+ out.push_str("../");
+ }
+ out.push_str(self.dir.as_str());
+ if out.is_empty() {
+ out.push('.');
+ } else if out.ends_with('/') {
+ out.pop();
+ }
+ // TODO: I didn't finish
+ f.write_str(&out)
+ }
+}
+
+impl LocalSource {
+ pub fn resolve_under(&self, parent: &SubDir) -> Result<SubDir, SubDirEscapeError> {
+ let mut comps: Vec<&str> = parent.as_path().components().map(|c| c.as_str()).collect();
+ if self.ups > comps.len() {
+ return Err(SubDirEscapeError);
+ }
+ comps.truncate(comps.len() - self.ups);
+ let mut buf = Utf8PathBuf::from_iter(comps);
+ buf.push(self.dir.as_path());
+ SubDir::try_from(buf)
+ }
+}
+
+impl Serialize for LocalSource {
+ fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
+ #[derive(Serialize)]
+ struct JsonLocal<'a> {
+ directory: &'a str,
+ }
+ let rendered = self.to_string();
+ JsonLocal {
+ directory: &rendered,
+ }
+ .serialize(ser)
+ }
+}
+
+impl<'de> Deserialize<'de> for LocalSource {
+ fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
+ #[derive(Deserialize)]
+ struct JsonLocal {
+ directory: String,
+ }
+ let j = JsonLocal::deserialize(de)?;
+ LocalSource::from_str(&j.directory)
+ .map_err(|e| de::Error::custom(format!("invalid local path {:?}: {e}", j.directory)))
+ }
+}
+
+impl GitSource {
+ /// Repo path with the trailing `.git` (if any) stripped.
+ pub fn plain_repo_name(&self) -> SubDir {
+ self.repo.without_git_suffix()
+ }
+
+ /// Canonical install path: `host/user/repo[/subdir]`.
+ pub fn name(&self) -> SubDir {
+ let mut p = Utf8PathBuf::from(self.host.as_str());
+ p.push(self.plain_repo_name());
+ if !self.subdir.is_empty() {
+ p.push(self.subdir.as_path());
+ }
+ SubDir::try_from(p).expect("host + subdirs is a valid SubDir")
+ }
+
+ /// Last path component of `repo[/subdir]`, used as the legacy symlink name.
+ pub fn legacy_name(&self) -> String {
+ self.name()
+ .file_name()
+ .expect("name has at least one component")
+ .to_owned()
+ }
+
+ /// Git remote URL for cloning.
+ pub fn remote(&self) -> String {
+ let host = self.host.as_str();
+ let repo = self.repo.as_str();
+ match self.scheme {
+ GitScheme::Ssh => format!("ssh://git@{host}/{repo}"),
+ GitScheme::Https => format!("https://{host}/{repo}"),
+ }
+ }
+
+ /// Parse a URI like `github.com/user/repo/subdir@version` into a
+ /// `Dependency`.
+ pub fn parse(uri: &str) -> Option<Dependency> {
+ git_uri::parse(uri).ok()
+ }
+}
+
+peg::parser! {
+ grammar git_uri() for str {
+ rule host_segment() = ['a'..='z' | 'A'..='Z' | '0'..='9' | '-']+;
+ rule host() -> Hostname
+ = s:$(host_segment()++".")
+ { Hostname::from_str(s).expect("grammar restricted to valid host chars") }
+
+ // User/repo path segments. `~` is allowed for Bitbucket personal repos.
+ rule path_segment() = ['a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' | '~']+;
+ // Subdir segments allow dots (e.g. `ksonnet.beta.3`).
+ rule subdir_segment() = ['a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' | '.']+;
+
+ // `user[/group...]/repo.git`
+ rule repo_dotgit() -> SubDir
+ = s:$(path_segment()++"/" ".git")
+ { SubDir::from_str(s).expect("grammar restricted to subpath chars") }
+ // `user/repo` (exactly two segments, no `.git`)
+ rule repo_simple() -> SubDir
+ = s:$(path_segment() "/" path_segment())
+ { SubDir::from_str(s).expect("grammar restricted to subpath chars") }
+
+ // Subdir starts with `/`. May be empty.
+ rule subdir() -> SubDir
+ = "/" s:$(subdir_segment() ** "/") "/"?
+ { SubDir::from_str(s).expect("grammar restricted to subdir chars") }
+ / { SubDir::empty() }
+
+ rule version() -> &'input str
+ = "@" v:$([_]+) { v }
+
+
+ // git@host:path.git[/subdir][@version] (SCP style)
+ rule scp_uri() -> Dependency
+ = "git@" h:host() ":" repo:repo_dotgit() subdir:subdir()
+ v:version()?
+ {
+ make_dep(GitScheme::Ssh, h, repo, subdir, v)
+ }
+
+ // ssh://git@host/path.git[/subdir][@version]
+ rule ssh_uri() -> Dependency
+ = "ssh://git@" h:host() "/" repo:repo_dotgit() subdir:subdir()
+ v:version()?
+ {
+ make_dep(GitScheme::Ssh, h, repo, subdir, v)
+ }
+
+ // [https://]host/path.git[/subdir][@version]
+ rule https_dotgit() -> Dependency
+ = "https://"? h:host() "/" repo:repo_dotgit() subdir:subdir()
+ v:version()?
+ {
+ make_dep(GitScheme::Https, h, repo, subdir, v)
+ }
+
+ // [https://]host/user/repo[/subdir[/...]][@version]
+ rule https_simple() -> Dependency
+ = "https://"? h:host() "/" repo:repo_simple() subdir:subdir()
+ v:version()?
+ {
+ make_dep(GitScheme::Https, h, repo, subdir, v)
+ }
+
+ pub rule parse() -> Dependency
+ = ssh_uri() / scp_uri() / https_dotgit() / https_simple()
+ }
+}
+
+fn make_dep(
+ scheme: GitScheme,
+ host: Hostname,
+ repo: SubDir,
+ subdir: SubDir,
+ version: Option<&str>,
+) -> Dependency {
+ Dependency {
+ source: Source::Git(GitSource {
+ scheme,
+ host,
+ repo,
+ subdir,
+ }),
+ version: version.map(str::to_owned),
+ sum: None,
+ name: None,
+ single: false,
+ }
+}
+
+impl Dependency {
+ /// Canonical install path for deduplication and vendor extraction.
+ pub fn canonical_name(&self) -> Utf8PathBuf {
+ match &self.source {
+ Source::Git(git) => git.name().into_inner(),
+ Source::Local(local) => Utf8PathBuf::from(local.to_string()),
+ }
+ }
+
+ /// Legacy symlink name: `dep.name` override, or last path component.
+ pub fn legacy_link_name(&self) -> String {
+ if let Some(name) = &self.name {
+ return name.clone();
+ }
+ match &self.source {
+ Source::Git(git) => git.legacy_name(),
+ Source::Local(local) => local
+ .dir
+ .file_name()
+ .map_or_else(|| local.to_string(), str::to_owned),
+ }
+ }
+}
+
+impl Serialize for GitSource {
+ fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+ #[derive(Serialize)]
+ struct JsonGit<'a> {
+ remote: String,
+ #[serde(skip_serializing_if = "str::is_empty")]
+ subdir: &'a str,
+ }
+ JsonGit {
+ remote: self.remote(),
+ subdir: self.subdir.as_str(),
+ }
+ .serialize(serializer)
+ }
+}
+
+impl<'de> Deserialize<'de> for GitSource {
+ fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+ #[derive(Deserialize)]
+ struct JsonGit {
+ remote: String,
+ #[serde(default)]
+ subdir: String,
+ }
+ let j = JsonGit::deserialize(deserializer)?;
+
+ let parsed = GitSource::parse(&j.remote)
+ .ok_or_else(|| de::Error::custom(format!("unable to parse git url {:?}", j.remote)))?;
+ let Source::Git(mut gs) = parsed.source else {
+ unreachable!()
+ };
+
+ if !j.subdir.is_empty() {
+ gs.subdir = SubDir::from_str(j.subdir.trim_start_matches('/'))
+ .map_err(|e| de::Error::custom(format!("invalid subdir {:?}: {e}", j.subdir)))?;
+ }
+
+ Ok(gs)
+ }
+}
+
+impl JsonnetFile {
+ pub fn load(path: &Path) -> Result<Self, Error> {
+ let data = std::fs::read(path).map_err(|e| Error::Io(path.to_owned(), e))?;
+ serde_json::from_slice(&data).map_err(Error::Json)
+ }
+}
+
+#[derive(Debug)]
+pub enum Error {
+ Io(std::path::PathBuf, std::io::Error),
+ Json(serde_json::Error),
+}
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Error::Io(path, e) => write!(f, "{}: {e}", path.display()),
+ Error::Json(e) => write!(f, "{e}"),
+ }
+ }
+}
+impl std::error::Error for Error {}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn host(s: &str) -> Hostname {
+ Hostname::from_str(s).expect("test host")
+ }
+ fn sd(s: &str) -> SubDir {
+ SubDir::from_str(s).expect("test subdir")
+ }
+
+ #[test]
+ fn parse_basic() {
+ let input = r#"{
+ "version": 1,
+ "dependencies": [
+ {
+ "source": {
+ "git": {
+ "remote": "https://github.com/grafana/jsonnet-libs.git",
+ "subdir": "grafana-builder"
+ }
+ },
+ "version": "54865853ebc1f901964e25a2e7a0e4d2cb6b9648",
+ "sum": "ELsYwK+kGdzX1mee2Yy+/b2mdO4Y503BOCDkFzwmGbE="
+ }
+ ],
+ "legacyImports": false
+ }"#;
+
+ let jf: JsonnetFile = serde_json::from_str(input).unwrap();
+ assert_eq!(jf.version, 1);
+ assert!(!jf.legacy_imports);
+ assert_eq!(jf.dependencies.len(), 1);
+
+ let dep = &jf.dependencies[0];
+ let Source::Git(git) = &dep.source else {
+ panic!("expected git source");
+ };
+ assert_eq!(git.host, "github.com");
+ assert_eq!(git.repo, "grafana/jsonnet-libs.git");
+ assert_eq!(git.subdir, "grafana-builder");
+ assert_eq!(
+ git.name(),
+ "github.com/grafana/jsonnet-libs/grafana-builder"
+ );
+ assert_eq!(git.legacy_name(), "grafana-builder");
+ assert_eq!(git.remote(), "https://github.com/grafana/jsonnet-libs.git");
+ assert_eq!(
+ dep.version.as_deref(),
+ Some("54865853ebc1f901964e25a2e7a0e4d2cb6b9648")
+ );
+ }
+
+ #[test]
+ fn parse_local_source() {
+ let input = r#"{
+ "version": 1,
+ "dependencies": [
+ {
+ "source": {
+ "local": { "directory": "../shared-lib" }
+ },
+ "version": ""
+ }
+ ]
+ }"#;
+
+ let jf: JsonnetFile = serde_json::from_str(input).unwrap();
+ let dep = &jf.dependencies[0];
+ let Source::Local(local) = &dep.source else {
+ panic!("expected local source");
+ };
+ assert_eq!(local.ups, 1);
+ assert_eq!(local.dir, "shared-lib");
+ assert_eq!(local.to_string(), "../shared-lib");
+ assert!(jf.legacy_imports);
+ }
+
+ #[test]
+ fn parse_uri_github_slug() {
+ let dep = GitSource::parse("github.com/ksonnet/ksonnet-lib/ksonnet.beta.3").unwrap();
+ let Source::Git(gs) = &dep.source else {
+ panic!()
+ };
+ assert_eq!(gs.scheme, GitScheme::Https);
+ assert_eq!(gs.host, "github.com");
+ assert_eq!(gs.repo, "ksonnet/ksonnet-lib");
+ assert_eq!(gs.subdir, "ksonnet.beta.3");
+ assert_eq!(dep.version, None);
+ assert_eq!(gs.remote(), "https://github.com/ksonnet/ksonnet-lib");
+ }
+
+ #[test]
+ fn parse_uri_ssh() {
+ let dep = GitSource::parse("ssh://git@example.com/user/repo.git/foobar@v1").unwrap();
+ let Source::Git(gs) = &dep.source else {
+ panic!()
+ };
+ assert_eq!(gs.scheme, GitScheme::Ssh);
+ assert_eq!(gs.host, "example.com");
+ assert_eq!(gs.repo, "user/repo.git");
+ assert_eq!(gs.subdir, "foobar");
+ assert_eq!(dep.version.as_deref(), Some("v1"));
+ assert_eq!(gs.remote(), "ssh://git@example.com/user/repo.git");
+ }
+
+ #[test]
+ fn parse_uri_scp() {
+ let dep = GitSource::parse("git@my.host:user/repo.git/foobar@v1").unwrap();
+ let Source::Git(gs) = &dep.source else {
+ panic!()
+ };
+ assert_eq!(gs.scheme, GitScheme::Ssh);
+ assert_eq!(gs.host, "my.host");
+ assert_eq!(gs.subdir, "foobar");
+ assert_eq!(dep.version.as_deref(), Some("v1"));
+ assert_eq!(gs.remote(), "ssh://git@my.host/user/repo.git");
+ }
+
+ #[test]
+ fn parse_uri_https_explicit() {
+ let dep = GitSource::parse("https://example.com/foo/bar").unwrap();
+ let Source::Git(gs) = &dep.source else {
+ panic!()
+ };
+ assert_eq!(gs.scheme, GitScheme::Https);
+ assert_eq!(gs.host, "example.com");
+ assert_eq!(gs.repo, "foo/bar");
+ assert_eq!(gs.subdir, "");
+ assert_eq!(gs.remote(), "https://example.com/foo/bar");
+ }
+
+ #[test]
+ fn parse_uri_no_scheme() {
+ let dep = GitSource::parse("example.com/foo/bar").unwrap();
+ let Source::Git(gs) = &dep.source else {
+ panic!()
+ };
+ assert_eq!(gs.scheme, GitScheme::Https);
+ assert_eq!(gs.host, "example.com");
+ assert_eq!(gs.remote(), "https://example.com/foo/bar");
+ }
+
+ #[test]
+ fn parse_uri_path_and_version() {
+ let dep = GitSource::parse("example.com/foo/bar/baz@bat").unwrap();
+ let Source::Git(gs) = &dep.source else {
+ panic!()
+ };
+ assert_eq!(gs.repo, "foo/bar");
+ assert_eq!(gs.subdir, "baz");
+ assert_eq!(dep.version.as_deref(), Some("bat"));
+ }
+
+ #[test]
+ fn parse_uri_version_only() {
+ let dep = GitSource::parse("example.com/foo/bar@baz").unwrap();
+ let Source::Git(gs) = &dep.source else {
+ panic!()
+ };
+ assert_eq!(gs.repo, "foo/bar");
+ assert_eq!(gs.subdir, "");
+ assert_eq!(dep.version.as_deref(), Some("baz"));
+ }
+
+ #[test]
+ fn parse_uri_deep_path() {
+ let dep = GitSource::parse("example.com/foo/bar/baz/bat").unwrap();
+ let Source::Git(gs) = &dep.source else {
+ panic!()
+ };
+ assert_eq!(gs.repo, "foo/bar");
+ assert_eq!(gs.subdir, "baz/bat");
+ }
+
+ #[test]
+ fn parse_uri_subgroups() {
+ let dep = GitSource::parse("example.com/group/subgroup/repository.git").unwrap();
+ let Source::Git(gs) = &dep.source else {
+ panic!()
+ };
+ assert_eq!(gs.repo, "group/subgroup/repository.git");
+ assert_eq!(gs.plain_repo_name(), "group/subgroup/repository");
+ assert_eq!(gs.subdir, "");
+ assert_eq!(
+ gs.remote(),
+ "https://example.com/group/subgroup/repository.git"
+ );
+ }
+
+ #[test]
+ fn parse_uri_subgroup_subdir() {
+ let dep = GitSource::parse("example.com/group/subgroup/repository.git/subdir").unwrap();
+ let Source::Git(gs) = &dep.source else {
+ panic!()
+ };
+ assert_eq!(gs.plain_repo_name(), "group/subgroup/repository");
+ assert_eq!(gs.subdir, "subdir");
+ }
+
+ #[test]
+ fn parse_uri_bitbucket_personal() {
+ let dep = GitSource::parse("bitbucket.org/~user/repository.git").unwrap();
+ let Source::Git(gs) = &dep.source else {
+ panic!()
+ };
+ assert_eq!(gs.host, "bitbucket.org");
+ assert_eq!(gs.repo, "~user/repository.git");
+ assert_eq!(gs.remote(), "https://bitbucket.org/~user/repository.git");
+ }
+
+ #[test]
+ fn name_with_subdir() {
+ let gs = GitSource {
+ scheme: GitScheme::Https,
+ host: host("github.com"),
+ repo: sd("ksonnet/ksonnet-lib"),
+ subdir: sd("ksonnet.beta.3"),
+ };
+ assert_eq!(gs.name(), "github.com/ksonnet/ksonnet-lib/ksonnet.beta.3");
+ assert_eq!(gs.legacy_name(), "ksonnet.beta.3");
+ }
+
+ #[test]
+ fn name_without_subdir() {
+ let gs = GitSource {
+ scheme: GitScheme::Https,
+ host: host("github.com"),
+ repo: sd("user/repo"),
+ subdir: SubDir::empty(),
+ };
+ assert_eq!(gs.name(), "github.com/user/repo");
+ assert_eq!(gs.legacy_name(), "repo");
+ }
+
+ #[test]
+ fn defaults() {
+ let input = r#"{ "version": 1 }"#;
+ let jf: JsonnetFile = serde_json::from_str(input).unwrap();
+ assert!(jf.dependencies.is_empty());
+ assert!(jf.legacy_imports);
+ }
+
+ #[test]
+ fn roundtrip() {
+ let jf = JsonnetFile {
+ version: 1,
+ dependencies: vec![Dependency {
+ source: Source::Git(GitSource {
+ scheme: GitScheme::Https,
+ host: host("github.com"),
+ repo: sd("user/repo"),
+ subdir: sd("lib"),
+ }),
+ version: Some("main".into()),
+ sum: None,
+ name: None,
+ single: false,
+ }],
+ legacy_imports: false,
+ };
+ let json = serde_json::to_string_pretty(&jf).unwrap();
+ let parsed: JsonnetFile = serde_json::from_str(&json).unwrap();
+ assert_eq!(parsed.dependencies.len(), 1);
+ let Source::Git(gs) = &parsed.dependencies[0].source else {
+ panic!()
+ };
+ assert_eq!(gs.host, "github.com");
+ assert_eq!(gs.repo, "user/repo");
+ assert_eq!(gs.subdir, "lib");
+ }
+
+ #[test]
+ fn hostname_rejects_slash() {
+ assert!(Hostname::from_str("foo/bar").is_err());
+ assert!(Hostname::from_str("").is_err());
+ assert!(Hostname::from_str(".").is_err());
+ assert!(Hostname::from_str("..").is_err());
+ assert!(Hostname::from_str(".foo").is_err());
+ assert!(Hostname::from_str("foo.").is_err());
+ assert!(Hostname::from_str("foo..bar").is_err());
+ assert!(Hostname::from_str("foo bar").is_err());
+ assert!(Hostname::from_str("foo.bar").is_ok());
+ }
+
+ #[test]
+ fn subdir_rejects_escape() {
+ assert!(SubDir::from_str("../foo").is_err());
+ assert!(SubDir::from_str("/foo").is_err());
+ assert!(SubDir::from_str("foo/../bar").is_err());
+ assert!(SubDir::from_str("foo/bar").is_ok());
+ assert!(SubDir::from_str("").is_ok());
+ }
+
+ #[test]
+ fn local_source_parse() {
+ let l = LocalSource::from_str("../shared-lib").unwrap();
+ assert_eq!(l.ups, 1);
+ assert_eq!(l.dir, "shared-lib");
+
+ let l = LocalSource::from_str("../../foo/bar").unwrap();
+ assert_eq!(l.ups, 2);
+ assert_eq!(l.dir, "foo/bar");
+
+ let l = LocalSource::from_str("./foo").unwrap();
+ assert_eq!(l.ups, 0);
+ assert_eq!(l.dir, "foo");
+
+ let l = LocalSource::from_str(".").unwrap();
+ assert_eq!(l.ups, 0);
+ assert!(l.dir.is_empty());
+
+ let l = LocalSource::from_str("..").unwrap();
+ assert_eq!(l.ups, 1);
+ assert!(l.dir.is_empty());
+
+ // Mid-path `..` is rejected.
+ assert!(LocalSource::from_str("foo/../bar").is_err());
+ // Absolute path is rejected.
+ assert!(LocalSource::from_str("/foo").is_err());
+ }
+
+ #[test]
+ fn local_source_render_roundtrip() {
+ for s in ["../shared-lib", "../../foo/bar", "foo", "."] {
+ assert_eq!(LocalSource::from_str(s).unwrap().to_string(), s);
+ }
+ }
+
+ #[test]
+ fn local_source_resolve_under() {
+ // `../foo` from `pkg/sub` lands at `pkg/foo`.
+ let l = LocalSource::from_str("../foo").unwrap();
+ assert_eq!(l.resolve_under(&sd("pkg/sub")).unwrap(), "pkg/foo");
+
+ // Plain `foo` from `pkg/sub` lands at `pkg/sub/foo`.
+ let l = LocalSource::from_str("foo").unwrap();
+ assert_eq!(l.resolve_under(&sd("pkg/sub")).unwrap(), "pkg/sub/foo");
+
+ // Too many `..` escapes the parent.
+ let l = LocalSource::from_str("../../../foo").unwrap();
+ assert!(l.resolve_under(&sd("pkg")).is_err());
+ }
+}
crates/jrsonnet-pkg/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-pkg/src/lib.rs
@@ -0,0 +1,2 @@
+pub mod install;
+pub mod jsonnet_bundler;