eh: add info command; bump deps
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I85faac1cc3a48ed2622c1160ab954d8f6a6a6964
This commit is contained in:
parent
7e2338b017
commit
8836eacb95
9 changed files with 660 additions and 42 deletions
296
Cargo.lock
generated
296
Cargo.lock
generated
|
|
@ -13,15 +13,21 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.13"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.102"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.10.0"
|
version = "2.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
|
|
@ -78,13 +84,12 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "console"
|
name = "console"
|
||||||
version = "0.16.2"
|
version = "0.16.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4"
|
checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"encode_unicode",
|
"encode_unicode",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
@ -107,8 +112,10 @@ dependencies = [
|
||||||
"dialoguer",
|
"dialoguer",
|
||||||
"eh-log",
|
"eh-log",
|
||||||
"regex",
|
"regex",
|
||||||
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
"textwrap",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"yansi",
|
"yansi",
|
||||||
|
|
@ -127,6 +134,12 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.14"
|
version = "0.3.14"
|
||||||
|
|
@ -144,17 +157,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "foldhash"
|
||||||
version = "0.3.4"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"r-efi",
|
"r-efi",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
|
"wasip3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||||
|
dependencies = [
|
||||||
|
"foldhash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
|
@ -162,16 +197,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "id-arena"
|
||||||
version = "1.0.17"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown 0.16.1",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "leb128fmt"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.183"
|
version = "0.2.184"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
|
|
@ -180,16 +239,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "log"
|
||||||
version = "2.7.6"
|
version = "0.4.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.3"
|
version = "1.21.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prettyplease"
|
||||||
|
version = "0.2.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
|
|
@ -211,9 +286,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "r-efi"
|
name = "r-efi"
|
||||||
version = "5.3.0"
|
version = "6.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
|
|
@ -229,9 +304,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.13"
|
version = "0.4.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|
@ -240,9 +315,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.8"
|
version = "0.8.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
|
|
@ -266,6 +341,12 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.228"
|
version = "1.0.228"
|
||||||
|
|
@ -273,6 +354,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -314,6 +396,12 @@ version = "1.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
|
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smawk"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.117"
|
version = "2.0.117"
|
||||||
|
|
@ -338,6 +426,17 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.16.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
|
||||||
|
dependencies = [
|
||||||
|
"smawk",
|
||||||
|
"unicode-linebreak",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.18"
|
version = "2.0.18"
|
||||||
|
|
@ -360,9 +459,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.22"
|
version = "1.0.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-linebreak"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
|
|
@ -370,6 +475,12 @@ version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
|
@ -389,6 +500,49 @@ dependencies = [
|
||||||
"wit-bindgen",
|
"wit-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasip3"
|
||||||
|
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-encoder"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||||
|
dependencies = [
|
||||||
|
"leb128fmt",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-metadata"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"indexmap",
|
||||||
|
"wasm-encoder",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasmparser"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"hashbrown 0.15.5",
|
||||||
|
"indexmap",
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-util"
|
name = "winapi-util"
|
||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
|
|
@ -418,6 +572,88 @@ name = "wit-bindgen"
|
||||||
version = "0.51.0"
|
version = "0.51.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen-rust-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-core"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"heck",
|
||||||
|
"wit-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rust"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"heck",
|
||||||
|
"indexmap",
|
||||||
|
"prettyplease",
|
||||||
|
"syn",
|
||||||
|
"wasm-metadata",
|
||||||
|
"wit-bindgen-core",
|
||||||
|
"wit-component",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rust-macro"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wit-bindgen-core",
|
||||||
|
"wit-bindgen-rust",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-component"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bitflags",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"wasm-encoder",
|
||||||
|
"wasm-metadata",
|
||||||
|
"wasmparser",
|
||||||
|
"wit-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-parser"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"id-arena",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"unicode-xid",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xtask"
|
name = "xtask"
|
||||||
|
|
@ -436,6 +672,6 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.17"
|
version = "1.0.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439"
|
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,10 @@ clap = { default-features = false, features = [ "std", "help", "derive"
|
||||||
clap_complete = "4.6.0"
|
clap_complete = "4.6.0"
|
||||||
dialoguer = { default-features = false, version = "0.12.0" }
|
dialoguer = { default-features = false, version = "0.12.0" }
|
||||||
regex = "1.12.3"
|
regex = "1.12.3"
|
||||||
|
serde = { features = [ "derive" ], version = "1.0.149" }
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.149"
|
||||||
tempfile = "3.27.0"
|
tempfile = "3.27.0"
|
||||||
|
textwrap = "0.16.2"
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
yansi = "1.0.1"
|
yansi = "1.0.1"
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ enum Binary {
|
||||||
Ns,
|
Ns,
|
||||||
Nb,
|
Nb,
|
||||||
Nd,
|
Nd,
|
||||||
|
Ni,
|
||||||
Nu,
|
Nu,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,6 +54,7 @@ impl Binary {
|
||||||
Self::Ns => "ns",
|
Self::Ns => "ns",
|
||||||
Self::Nb => "nb",
|
Self::Nb => "nb",
|
||||||
Self::Nd => "nd",
|
Self::Nd => "nd",
|
||||||
|
Self::Ni => "ni",
|
||||||
Self::Nu => "nu",
|
Self::Nu => "nu",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -94,8 +96,14 @@ fn create_multicall_binaries(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let multicall_binaries =
|
let multicall_binaries = [
|
||||||
[Binary::Nr, Binary::Ns, Binary::Nb, Binary::Nd, Binary::Nu];
|
Binary::Nr,
|
||||||
|
Binary::Ns,
|
||||||
|
Binary::Nb,
|
||||||
|
Binary::Nd,
|
||||||
|
Binary::Ni,
|
||||||
|
Binary::Nu,
|
||||||
|
];
|
||||||
let bin_path = Path::new(bin_dir);
|
let bin_path = Path::new(bin_dir);
|
||||||
|
|
||||||
for binary in multicall_binaries {
|
for binary in multicall_binaries {
|
||||||
|
|
@ -158,7 +166,7 @@ fn generate_completions(
|
||||||
println!("completion file generated: {}", completion_file.display());
|
println!("completion file generated: {}", completion_file.display());
|
||||||
|
|
||||||
// Create symlinks for multicall binaries
|
// Create symlinks for multicall binaries
|
||||||
let multicall_names = ["nb", "nd", "nr", "ns", "nu"];
|
let multicall_names = ["nb", "nd", "ni", "nr", "ns", "nu"];
|
||||||
for name in &multicall_names {
|
for name in &multicall_names {
|
||||||
let symlink_path = output_dir.join(format!("{name}.{shell}"));
|
let symlink_path = output_dir.join(format!("{name}.{shell}"));
|
||||||
if symlink_path.exists() {
|
if symlink_path.exists() {
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,10 @@ clap.workspace = true
|
||||||
dialoguer.workspace = true
|
dialoguer.workspace = true
|
||||||
eh-log.workspace = true
|
eh-log.workspace = true
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
|
textwrap.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
walkdir.workspace = true
|
walkdir.workspace = true
|
||||||
yansi.workspace = true
|
yansi.workspace = true
|
||||||
|
|
|
||||||
290
eh/src/commands/info.rs
Normal file
290
eh/src/commands/info.rs
Normal file
|
|
@ -0,0 +1,290 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use eh_log::{log_error, log_info};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use yansi::Paint;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
commands::NixCommand,
|
||||||
|
error::{EhError, Result},
|
||||||
|
util::{make_eval_expr, print_error_suggestions},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct PackageMeta {
|
||||||
|
name: String,
|
||||||
|
version: Option<String>,
|
||||||
|
description: Option<String>,
|
||||||
|
long_description: Option<String>,
|
||||||
|
license: Option<serde_json::Value>,
|
||||||
|
homepage: Option<String>,
|
||||||
|
platforms: Option<Vec<String>>,
|
||||||
|
broken: Option<bool>,
|
||||||
|
insecure: Option<bool>,
|
||||||
|
#[serde(rename = "unfree")]
|
||||||
|
unfree: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct PackageOutputs {
|
||||||
|
#[serde(flatten)]
|
||||||
|
outputs: HashMap<String, serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_info(args: &[String]) -> Result<i32> {
|
||||||
|
// Get the package argument (skip flags)
|
||||||
|
let pkg = args
|
||||||
|
.iter()
|
||||||
|
.find(|arg| !arg.starts_with('-'))
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| ".".to_string());
|
||||||
|
|
||||||
|
let eval_arg = make_eval_expr(&pkg);
|
||||||
|
let pkg_name: String = if eval_arg.contains("#") {
|
||||||
|
eval_arg
|
||||||
|
.split("#")
|
||||||
|
.last()
|
||||||
|
.unwrap_or(&eval_arg)
|
||||||
|
.trim_end_matches(".meta")
|
||||||
|
.to_string()
|
||||||
|
} else {
|
||||||
|
eval_arg.trim_end_matches(".meta").to_string()
|
||||||
|
};
|
||||||
|
// Handle .# case - show "default" as the package name
|
||||||
|
let pkg_name = if pkg_name.is_empty() {
|
||||||
|
"default".to_string()
|
||||||
|
} else {
|
||||||
|
pkg_name
|
||||||
|
};
|
||||||
|
|
||||||
|
log_info!("Fetching info for {}", pkg_name.bold());
|
||||||
|
|
||||||
|
// Fetch metadata
|
||||||
|
let meta_cmd = NixCommand::new("eval")
|
||||||
|
.arg("--json")
|
||||||
|
.arg(&eval_arg)
|
||||||
|
.print_build_logs(false);
|
||||||
|
|
||||||
|
let meta_output = meta_cmd.output()?;
|
||||||
|
|
||||||
|
if !meta_output.status.success() {
|
||||||
|
log_error!("Failed to fetch package info");
|
||||||
|
print_error_suggestions(&meta_output.stderr);
|
||||||
|
return Err(EhError::NixCommandFailed {
|
||||||
|
command: "eval".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let meta: PackageMeta =
|
||||||
|
serde_json::from_slice(&meta_output.stdout).map_err(|e| {
|
||||||
|
EhError::Io(std::io::Error::other(format!(
|
||||||
|
"Failed to parse package metadata: {}",
|
||||||
|
e
|
||||||
|
)))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Fetch outputs
|
||||||
|
let outputs_expr = eval_arg
|
||||||
|
.strip_suffix(".meta")
|
||||||
|
.unwrap_or(&eval_arg)
|
||||||
|
.to_string();
|
||||||
|
let outputs_cmd = NixCommand::new("eval")
|
||||||
|
.arg("--json")
|
||||||
|
.arg(format!("{}.outputs", outputs_expr))
|
||||||
|
.print_build_logs(false);
|
||||||
|
|
||||||
|
let outputs_output = outputs_cmd.output()?;
|
||||||
|
let outputs: Option<PackageOutputs> = if outputs_output.status.success() {
|
||||||
|
serde_json::from_slice(&outputs_output.stdout).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Print formatted info
|
||||||
|
print_package_info(&meta, outputs.as_ref(), &pkg);
|
||||||
|
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_package_info(
|
||||||
|
meta: &PackageMeta,
|
||||||
|
outputs: Option<&PackageOutputs>,
|
||||||
|
pkg_ref: &str,
|
||||||
|
) {
|
||||||
|
println!();
|
||||||
|
|
||||||
|
// Header
|
||||||
|
println!(" {} {}", "Package:".bold(), meta.name);
|
||||||
|
|
||||||
|
if let Some(ref version) = meta.version {
|
||||||
|
println!(" {} {}", "Version:".bold(), version);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref desc) = meta.description {
|
||||||
|
println!(" {} {}", "Description:".bold(), desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show long description if available and different from short description
|
||||||
|
if let Some(ref long_desc) = meta.long_description {
|
||||||
|
let should_show = meta
|
||||||
|
.description
|
||||||
|
.as_ref()
|
||||||
|
.map(|d| d != long_desc)
|
||||||
|
.unwrap_or(true);
|
||||||
|
if should_show {
|
||||||
|
println!();
|
||||||
|
// Wrap long description to 70 chars for readability
|
||||||
|
let wrapped = textwrap::fill(long_desc, 70);
|
||||||
|
for line in wrapped.lines() {
|
||||||
|
println!(" {}", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// License
|
||||||
|
if let Some(ref license) = meta.license {
|
||||||
|
let license_str = match license {
|
||||||
|
serde_json::Value::String(s) => s.clone(),
|
||||||
|
serde_json::Value::Object(obj) => {
|
||||||
|
obj
|
||||||
|
.get("spdxId")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.or_else(|| obj.get("shortName").and_then(|v| v.as_str()))
|
||||||
|
.unwrap_or("Unknown")
|
||||||
|
.to_string()
|
||||||
|
},
|
||||||
|
serde_json::Value::Array(licenses) => {
|
||||||
|
// Handle multiple licenses (e.g., neovim has Apache-2.0 AND Vim)
|
||||||
|
let license_names: Vec<String> = licenses
|
||||||
|
.iter()
|
||||||
|
.filter_map(|lic| {
|
||||||
|
match lic {
|
||||||
|
serde_json::Value::Object(obj) => {
|
||||||
|
obj
|
||||||
|
.get("spdxId")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.or_else(|| obj.get("shortName").and_then(|v| v.as_str()))
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
},
|
||||||
|
serde_json::Value::String(s) => Some(s.clone()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if license_names.is_empty() {
|
||||||
|
"Unknown".to_string()
|
||||||
|
} else {
|
||||||
|
license_names.join(", ")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => "Unknown".to_string(),
|
||||||
|
};
|
||||||
|
println!(" {} {}", "License:".bold(), license_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Homepage
|
||||||
|
if let Some(ref homepage) = meta.homepage {
|
||||||
|
println!(" {} {}", "Homepage:".bold(), homepage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meta section
|
||||||
|
println!();
|
||||||
|
println!(" {}", "Meta:".bold());
|
||||||
|
|
||||||
|
// Status indicators
|
||||||
|
let mut status_parts = Vec::new();
|
||||||
|
if meta.broken == Some(true) {
|
||||||
|
status_parts.push("Broken".red().to_string());
|
||||||
|
}
|
||||||
|
if meta.insecure == Some(true) {
|
||||||
|
status_parts.push("Insecure".red().to_string());
|
||||||
|
}
|
||||||
|
if meta.unfree == Some(true) {
|
||||||
|
status_parts.push("Unfree".yellow().to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if status_parts.is_empty() {
|
||||||
|
println!(" {} {}", "Status:".bold(), "✓ Available".green());
|
||||||
|
} else {
|
||||||
|
println!(" {} {}", "Status:".bold(), status_parts.join(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Platforms
|
||||||
|
if let Some(ref platforms) = meta.platforms {
|
||||||
|
let platform_list: Vec<_> = platforms.iter().take(4).cloned().collect();
|
||||||
|
let platform_str = if platforms.len() > 4 {
|
||||||
|
format!(
|
||||||
|
"{} + {} more",
|
||||||
|
platform_list.join(", "),
|
||||||
|
platforms.len() - 4
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
platform_list.join(", ")
|
||||||
|
};
|
||||||
|
println!(" {} {}", "Platforms:".bold(), platform_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outputs section
|
||||||
|
if let Some(outputs) = outputs {
|
||||||
|
println!();
|
||||||
|
println!(" {}", "Outputs:".bold());
|
||||||
|
let output_names: Vec<_> = outputs.outputs.keys().cloned().collect();
|
||||||
|
for name in output_names {
|
||||||
|
let marker = if name == "out" { " (default)" } else { "" };
|
||||||
|
println!(" • {}{}", name, marker.dim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage section
|
||||||
|
println!();
|
||||||
|
println!(" {}", "Usage:".bold());
|
||||||
|
println!(
|
||||||
|
" {} {} {}",
|
||||||
|
"eh run".dim(),
|
||||||
|
pkg_ref,
|
||||||
|
"# Run the package".dim()
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" {} {} {}",
|
||||||
|
"eh shell".dim(),
|
||||||
|
pkg_ref,
|
||||||
|
"# Enter shell with package".dim()
|
||||||
|
);
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_package_meta_deserialization() {
|
||||||
|
let json = r#"{
|
||||||
|
"name": "hello",
|
||||||
|
"version": "2.12.1",
|
||||||
|
"description": "A greeting program",
|
||||||
|
"license": "GPL-3.0",
|
||||||
|
"homepage": "https://example.com",
|
||||||
|
"platforms": ["x86_64-linux"],
|
||||||
|
"broken": false,
|
||||||
|
"insecure": false,
|
||||||
|
"unfree": false
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let meta: PackageMeta = serde_json::from_str(json).unwrap();
|
||||||
|
assert_eq!(meta.name, "hello");
|
||||||
|
assert_eq!(meta.version, Some("2.12.1".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_license_object_parsing() {
|
||||||
|
let json = r#"{
|
||||||
|
"name": "test",
|
||||||
|
"license": {"spdxId": "MIT", "fullName": "MIT License"}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let meta: PackageMeta = serde_json::from_str(json).unwrap();
|
||||||
|
assert!(meta.license.is_some());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod info;
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
|
||||||
const DEFAULT_BUFFER_SIZE: usize = 4096;
|
const DEFAULT_BUFFER_SIZE: usize = 4096;
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,11 @@ pub enum Command {
|
||||||
#[arg(trailing_var_arg = true)]
|
#[arg(trailing_var_arg = true)]
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
},
|
},
|
||||||
|
/// Show package information
|
||||||
|
Info {
|
||||||
|
#[arg(trailing_var_arg = true)]
|
||||||
|
args: Vec<String>,
|
||||||
|
},
|
||||||
/// Update flake inputs interactively
|
/// Update flake inputs interactively
|
||||||
Update {
|
Update {
|
||||||
#[arg(trailing_var_arg = true)]
|
#[arg(trailing_var_arg = true)]
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ fn handle_command(command: &str, args: &[String]) -> error::Result<i32> {
|
||||||
let classifier = util::DefaultNixErrorClassifier;
|
let classifier = util::DefaultNixErrorClassifier;
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
|
"info" => commands::info::handle_info(args),
|
||||||
|
|
||||||
"update" => commands::update::handle_update(args),
|
"update" => commands::update::handle_update(args),
|
||||||
"run" | "shell" | "build" | "develop" => {
|
"run" | "shell" | "build" | "develop" => {
|
||||||
commands::handle_nix_command(
|
commands::handle_nix_command(
|
||||||
|
|
@ -56,6 +58,7 @@ fn dispatch_multicall(
|
||||||
"ns" => "shell",
|
"ns" => "shell",
|
||||||
"nb" => "build",
|
"nb" => "build",
|
||||||
"nd" => "develop",
|
"nd" => "develop",
|
||||||
|
"ni" => "info",
|
||||||
"nu" => "update",
|
"nu" => "update",
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
@ -107,6 +110,8 @@ fn run_app() -> error::Result<i32> {
|
||||||
|
|
||||||
Some(Command::Develop { args }) => handle_command("develop", &args),
|
Some(Command::Develop { args }) => handle_command("develop", &args),
|
||||||
|
|
||||||
|
Some(Command::Info { args }) => handle_command("info", &args),
|
||||||
|
|
||||||
Some(Command::Update { args }) => handle_command("update", &args),
|
Some(Command::Update { args }) => handle_command("update", &args),
|
||||||
|
|
||||||
None => {
|
None => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use std::{
|
use std::{
|
||||||
io::{BufWriter, Write},
|
io::{BufWriter, IsTerminal, Write},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::LazyLock,
|
sync::LazyLock,
|
||||||
};
|
};
|
||||||
|
|
@ -42,6 +42,13 @@ static HASH_FIX_PATTERNS: LazyLock<[Regex; 3]> = LazyLock::new(|| {
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Regex to extract suggestions from Nix's "Did you mean" error line.
|
||||||
|
/// Matches patterns like:
|
||||||
|
/// - "Did you mean one of hello, world, or foo?"
|
||||||
|
/// - "Did you mean lib.hello?"
|
||||||
|
static DID_YOU_MEAN_PATTERN: LazyLock<Regex> =
|
||||||
|
LazyLock::new(|| Regex::new(r#"Did you mean (?:one of )?(.+?)\?"#).unwrap());
|
||||||
|
|
||||||
/// Trait for extracting store paths and hashes from nix output.
|
/// Trait for extracting store paths and hashes from nix output.
|
||||||
pub trait HashExtractor {
|
pub trait HashExtractor {
|
||||||
/// Extract the new store path/hash from nix output.
|
/// Extract the new store path/hash from nix output.
|
||||||
|
|
@ -311,9 +318,20 @@ fn is_hash_mismatch_error(stderr: &str) -> bool {
|
||||||
|
|
||||||
/// Construct the eval expression for a given argument.
|
/// Construct the eval expression for a given argument.
|
||||||
/// Handles both plain package names and flake references.
|
/// Handles both plain package names and flake references.
|
||||||
fn make_eval_expr(eval_arg: &str) -> String {
|
pub fn make_eval_expr(eval_arg: &str) -> String {
|
||||||
|
// Handle . (current directory) as .# (default package of current flake)
|
||||||
|
// Nix treats `nix build .` and `nix build .#` as equivalent
|
||||||
|
let eval_arg = if eval_arg == "." { ".#" } else { eval_arg };
|
||||||
|
|
||||||
if eval_arg.contains('#') {
|
if eval_arg.contains('#') {
|
||||||
|
// Handle .# (current flake default package) case
|
||||||
|
// .# needs to become .#default for meta evaluation to work
|
||||||
|
// because .#.meta evaluates 'meta' on the flake itself, not the package
|
||||||
|
if eval_arg.ends_with('#') {
|
||||||
|
format!("{eval_arg}default.meta")
|
||||||
|
} else {
|
||||||
format!("{eval_arg}.meta")
|
format!("{eval_arg}.meta")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
format!("nixpkgs#{eval_arg}.meta")
|
format!("nixpkgs#{eval_arg}.meta")
|
||||||
}
|
}
|
||||||
|
|
@ -512,18 +530,26 @@ pub fn handle_nix_with_retry(
|
||||||
if let Some(new_hash) = hash_extractor.extract_hash(&stderr) {
|
if let Some(new_hash) = hash_extractor.extract_hash(&stderr) {
|
||||||
let old_hash = hash_extractor.extract_old_hash(&stderr);
|
let old_hash = hash_extractor.extract_old_hash(&stderr);
|
||||||
|
|
||||||
// Ask for confirmation before fixing hash
|
// Ask for confirmation before fixing hash (skip in non-interactive mode)
|
||||||
let should_fix = dialoguer::Confirm::new()
|
let should_fix = if std::io::stdin().is_terminal() {
|
||||||
|
dialoguer::Confirm::new()
|
||||||
.with_prompt(format!(
|
.with_prompt(format!(
|
||||||
"Hash mismatch detected for {}. Update hash in local .nix files?",
|
"Hash mismatch detected for {}. Update hash in local .nix files?",
|
||||||
pkg.bold()
|
pkg.bold()
|
||||||
))
|
))
|
||||||
.default(true)
|
.default(true)
|
||||||
.interact()
|
.interact()
|
||||||
.map_err(|e| EhError::Io(std::io::Error::other(e)))?;
|
.map_err(|e| EhError::Io(std::io::Error::other(e)))?
|
||||||
|
} else {
|
||||||
|
log_warn!(
|
||||||
|
"{}: hash mismatch detected in non-interactive mode, skipping auto-fix",
|
||||||
|
pkg.bold()
|
||||||
|
);
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
if !should_fix {
|
if !should_fix {
|
||||||
log_warn!("{}: hash fix cancelled by user", pkg.bold());
|
log_warn!("{}: hash fix cancelled", pkg.bold());
|
||||||
return Err(EhError::ProcessExit { code: 1 });
|
return Err(EhError::ProcessExit { code: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -592,6 +618,9 @@ pub fn handle_nix_with_retry(
|
||||||
.write_all(&output.stderr)
|
.write_all(&output.stderr)
|
||||||
.map_err(EhError::Io)?;
|
.map_err(EhError::Io)?;
|
||||||
|
|
||||||
|
// Print contextual suggestions for common errors
|
||||||
|
print_error_suggestions(&output.stderr);
|
||||||
|
|
||||||
match output.status.code() {
|
match output.status.code() {
|
||||||
Some(code) => Err(EhError::ProcessExit { code }),
|
Some(code) => Err(EhError::ProcessExit { code }),
|
||||||
// No exit code means the process was killed by a signal
|
// No exit code means the process was killed by a signal
|
||||||
|
|
@ -611,6 +640,46 @@ impl NixErrorClassifier for DefaultNixErrorClassifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse suggestions from Nix's "Did you mean" error line.
|
||||||
|
/// Input: "Did you mean one of neovim, hevi, navi, neo or neo4j?"
|
||||||
|
/// Output: vec!["neovim", "hevi", "navi", "neo", "neo4j"]
|
||||||
|
fn parse_nix_suggestions(did_you_mean_line: &str) -> Vec<String> {
|
||||||
|
DID_YOU_MEAN_PATTERN
|
||||||
|
.captures(did_you_mean_line)
|
||||||
|
.and_then(|caps| caps.get(1))
|
||||||
|
.map(|m| m.as_str())
|
||||||
|
.map(|suggestions| {
|
||||||
|
suggestions
|
||||||
|
.split(", ")
|
||||||
|
.flat_map(|part| part.split(" or "))
|
||||||
|
.map(|s| s.trim().to_string())
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print contextual error suggestions when a command fails.
|
||||||
|
/// Parses Nix's own "Did you mean" suggestions from stderr and presents them
|
||||||
|
/// nicely to the user.
|
||||||
|
pub fn print_error_suggestions(stderr: &[u8]) {
|
||||||
|
let stderr_str = String::from_utf8_lossy(stderr);
|
||||||
|
|
||||||
|
// Look for Nix's "Did you mean" line in the error output
|
||||||
|
if let Some(line) = stderr_str.lines().find(|l| l.contains("Did you mean")) {
|
||||||
|
let suggestions = parse_nix_suggestions(line);
|
||||||
|
|
||||||
|
if !suggestions.is_empty() {
|
||||||
|
let formatted = suggestions
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.bold().to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
log_info!("Did you mean: {}?", formatted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue