git.delta.rocks / jrsonnet / refs/commits / 7cd3ab4b3483

difftreelog

style fix clippy warnings

Yaroslav Bolyukin2024-08-26parent: #7cdcae3.patch.diff
in: master

19 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -41,9 +41,9 @@
 
 [[package]]
 name = "anstream"
-version = "0.6.14"
+version = "0.6.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
+checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
 dependencies = [
  "anstyle",
  "anstyle-parse",
@@ -56,36 +56,36 @@
 
 [[package]]
 name = "anstyle"
-version = "1.0.7"
+version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
+checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
 
 [[package]]
 name = "anstyle-parse"
-version = "0.2.4"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
+checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
 dependencies = [
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle-query"
-version = "1.0.3"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
+checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
 dependencies = [
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "3.0.3"
+version = "3.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
+checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
 dependencies = [
  "anstyle",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -114,9 +114,9 @@
 
 [[package]]
 name = "bitflags"
-version = "2.5.0"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 
 [[package]]
 name = "block-buffer"
@@ -134,10 +134,19 @@
 checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
 
 [[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
 name = "cc"
-version = "1.0.97"
+version = "1.1.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
+checksum = "50d2eb3cd3d1bf4529e31c215ee6f93ec5a3d536d9f578f93d9d33ee19562932"
+dependencies = [
+ "shlex",
+]
 
 [[package]]
 name = "cfg-if"
@@ -147,9 +156,9 @@
 
 [[package]]
 name = "clap"
-version = "4.5.4"
+version = "4.5.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
+checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -157,9 +166,9 @@
 
 [[package]]
 name = "clap_builder"
-version = "4.5.2"
+version = "4.5.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
+checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
 dependencies = [
  "anstream",
  "anstyle",
@@ -169,36 +178,36 @@
 
 [[package]]
 name = "clap_complete"
-version = "4.5.2"
+version = "4.5.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e"
+checksum = "531d7959c5bbb6e266cecdd0f20213639c3a5c3e4d615f97db87661745f781ff"
 dependencies = [
  "clap",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.5.4"
+version = "4.5.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
+checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
 dependencies = [
  "heck",
  "proc-macro2",
  "quote",
- "syn 2.0.64",
+ "syn 2.0.76",
 ]
 
 [[package]]
 name = "clap_lex"
-version = "0.7.0"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
 
 [[package]]
 name = "colorchoice"
-version = "1.0.1"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
+checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
 
 [[package]]
 name = "console"
@@ -209,7 +218,7 @@
  "encode_unicode",
  "lazy_static",
  "libc",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -220,9 +229,9 @@
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.12"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
 dependencies = [
  "libc",
 ]
@@ -272,7 +281,7 @@
 dependencies = [
  "anyhow",
  "bumpalo",
- "indexmap 2.2.6",
+ "indexmap 2.4.0",
  "rustc-hash",
  "serde",
  "unicode-width",
@@ -286,9 +295,9 @@
 
 [[package]]
 name = "either"
-version = "1.12.0"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
 
 [[package]]
 name = "encode_unicode"
@@ -309,14 +318,14 @@
 checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
 dependencies = [
  "libc",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "fastrand"
-version = "2.1.0"
+version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
 
 [[package]]
 name = "fnv"
@@ -392,9 +401,9 @@
 
 [[package]]
 name = "indexmap"
-version = "2.2.6"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
 dependencies = [
  "equivalent",
  "hashbrown 0.14.5",
@@ -421,9 +430,9 @@
 
 [[package]]
 name = "is_terminal_polyfill"
-version = "1.70.0"
+version = "1.70.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
 
 [[package]]
 name = "itertools"
@@ -524,7 +533,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.64",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -542,7 +551,8 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.64",
+ "syn 2.0.76",
+ "syn-dissect-closure",
 ]
 
 [[package]]
@@ -620,15 +630,15 @@
 
 [[package]]
 name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "libc"
-version = "0.2.155"
+version = "0.2.158"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
 
 [[package]]
 name = "libjsonnet"
@@ -665,18 +675,18 @@
 
 [[package]]
 name = "logos"
-version = "0.14.0"
+version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "161971eb88a0da7ae0c333e1063467c5b5727e7fb6b710b8db4814eade3a42e8"
+checksum = "ff1ceb190eb9bdeecdd8f1ad6a71d6d632a50905948771718741b5461fb01e13"
 dependencies = [
  "logos-derive",
 ]
 
 [[package]]
 name = "logos-codegen"
-version = "0.14.0"
+version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e31badd9de5131fdf4921f6473d457e3dd85b11b7f091ceb50e4df7c3eeb12a"
+checksum = "90be66cb7bd40cb5cc2e9cfaf2d1133b04a3d93b72344267715010a466e0915a"
 dependencies = [
  "beef",
  "fnv",
@@ -684,23 +694,23 @@
  "proc-macro2",
  "quote",
  "regex-syntax",
- "syn 2.0.64",
+ "syn 2.0.76",
 ]
 
 [[package]]
 name = "logos-derive"
-version = "0.14.0"
+version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c2a69b3eb68d5bd595107c9ee58d7e07fe2bb5e360cc85b0f084dedac80de0a"
+checksum = "45154231e8e96586b39494029e58f12f8ffcb5ecf80333a603a13aa205ea8cbd"
 dependencies = [
  "logos-codegen",
 ]
 
 [[package]]
 name = "lru"
-version = "0.12.3"
+version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
+checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904"
 dependencies = [
  "hashbrown 0.14.5",
 ]
@@ -713,18 +723,9 @@
 
 [[package]]
 name = "memchr"
-version = "2.7.2"
+version = "2.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
-
-[[package]]
-name = "memoffset"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
-dependencies = [
- "autocfg",
-]
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
 
 [[package]]
 name = "mimalloc-sys"
@@ -747,9 +748,9 @@
 
 [[package]]
 name = "num-bigint"
-version = "0.4.5"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
 dependencies = [
  "num-integer",
  "num-traits",
@@ -811,9 +812,9 @@
 
 [[package]]
 name = "peg"
-version = "0.8.3"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a625d12ad770914cbf7eff6f9314c3ef803bfe364a1b20bc36ddf56673e71e5"
+checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f"
 dependencies = [
  "peg-macros",
  "peg-runtime",
@@ -821,9 +822,9 @@
 
 [[package]]
 name = "peg-macros"
-version = "0.8.3"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f241d42067ed3ab6a4fece1db720838e1418f36d868585a27931f95d6bc03582"
+checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426"
 dependencies = [
  "peg-runtime",
  "proc-macro2",
@@ -838,15 +839,18 @@
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.17"
+version = "0.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.82"
+version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
 dependencies = [
  "unicode-ident",
 ]
@@ -862,9 +866,9 @@
 
 [[package]]
 name = "quote"
-version = "1.0.36"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
 dependencies = [
  "proc-macro2",
 ]
@@ -919,18 +923,18 @@
 
 [[package]]
 name = "redox_syscall"
-version = "0.5.1"
+version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
+checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "regex"
-version = "1.10.4"
+version = "1.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -940,9 +944,9 @@
 
 [[package]]
 name = "regex-automata"
-version = "0.4.6"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -951,19 +955,18 @@
 
 [[package]]
 name = "regex-syntax"
-version = "0.8.3"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
 
 [[package]]
 name = "rowan"
-version = "0.15.15"
+version = "0.15.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
+checksum = "0a542b0253fa46e632d27a1dc5cf7b930de4df8659dc6e720b647fc72147ae3d"
 dependencies = [
  "countme",
  "hashbrown 0.14.5",
- "memoffset",
  "rustc-hash",
  "text-size",
 ]
@@ -984,7 +987,7 @@
  "errno",
  "libc",
  "linux-raw-sys",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -1001,31 +1004,32 @@
 
 [[package]]
 name = "serde"
-version = "1.0.202"
+version = "1.0.209"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
+checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.202"
+version = "1.0.209"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
+checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.64",
+ "syn 2.0.76",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.117"
+version = "1.0.127"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
+checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
 dependencies = [
  "itoa",
+ "memchr",
  "ryu",
  "serde",
 ]
@@ -1075,10 +1079,16 @@
 ]
 
 [[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
 name = "similar"
-version = "2.5.0"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
+checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
 
 [[package]]
 name = "smallvec"
@@ -1088,15 +1098,15 @@
 
 [[package]]
 name = "stacker"
-version = "0.1.15"
+version = "0.1.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
+checksum = "95a5daa25ea337c85ed954c0496e3bdd2c7308cc3b24cf7b50d04876654c579f"
 dependencies = [
  "cc",
  "cfg-if",
  "libc",
  "psm",
- "winapi",
+ "windows-sys 0.36.1",
 ]
 
 [[package]]
@@ -1124,9 +1134,9 @@
 
 [[package]]
 name = "syn"
-version = "2.0.64"
+version = "2.0.76"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f"
+checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1134,15 +1144,27 @@
 ]
 
 [[package]]
+name = "syn-dissect-closure"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "343bae741672e4b94421cbe93f9794ba9a061434272f7e3a29ff43be26be3ac9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.76",
+]
+
+[[package]]
 name = "tempfile"
-version = "3.10.1"
+version = "3.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
 dependencies = [
  "cfg-if",
  "fastrand",
+ "once_cell",
  "rustix",
- "windows-sys",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -1165,22 +1187,22 @@
 
 [[package]]
 name = "thiserror"
-version = "1.0.61"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.61"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.64",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -1203,21 +1225,21 @@
 
 [[package]]
 name = "unicode-width"
-version = "0.1.12"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
+checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
 
 [[package]]
 name = "utf8parse"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
 [[package]]
 name = "version_check"
-version = "0.9.4"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
 
 [[package]]
 name = "wasi"
@@ -1226,99 +1248,129 @@
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
-name = "winapi"
-version = "0.3.9"
+name = "windows-sys"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
 dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
+ "windows_aarch64_msvc 0.36.1",
+ "windows_i686_gnu 0.36.1",
+ "windows_i686_msvc 0.36.1",
+ "windows_x86_64_gnu 0.36.1",
+ "windows_x86_64_msvc 0.36.1",
 ]
 
 [[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
+name = "windows-sys"
+version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
 
 [[package]]
 name = "windows-sys"
-version = "0.52.0"
+version = "0.59.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
 dependencies = [
  "windows-targets",
 ]
 
 [[package]]
 name = "windows-targets"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
  "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
  "windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
  "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_x86_64_msvc 0.52.6",
 ]
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.52.5"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 
 [[package]]
 name = "windows_i686_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.52.5"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
 [[package]]
 name = "xshell"
@@ -1341,7 +1393,7 @@
 dependencies = [
  "anyhow",
  "clap",
- "indexmap 2.2.6",
+ "indexmap 2.4.0",
  "itertools",
  "proc-macro2",
  "quote",
@@ -1360,20 +1412,21 @@
 
 [[package]]
 name = "zerocopy"
-version = "0.7.34"
+version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
 dependencies = [
+ "byteorder",
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.7.34"
+version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.64",
+ "syn 2.0.76",
 ]
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -89,6 +89,7 @@
 lru = "0.12.3"
 
 json-structural-diff = "0.1.0"
+syn-dissect-closure = "0.1.0"
 
 [workspace.lints.rust]
 unsafe_op_in_unsafe_fn = "deny"
@@ -107,9 +108,9 @@
 all = "warn"
 
 [workspace.lints.clippy]
-all = "warn"
-nursery = "warn"
-pedantic = "warn"
+all = { level = "warn", priority = -1 }
+nursery = { level = "warn", priority = -1 }
+pedantic = { level = "warn", priority = -1 }
 
 ptr_arg = "allow"
 # Too verbose
modifiedbindings/jsonnet/src/native.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/native.rs
+++ b/bindings/jsonnet/src/native.rs
@@ -20,6 +20,7 @@
 /// - `ctx` User pointer, given in `jsonnet_native_callback`.
 /// - `argv` Array of arguments from Jsonnet code.
 /// - `param` success Set this byref param to 1 to indicate success and 0 for failure.
+///
 /// Returns the content of the imported file, or an error message.
 type JsonnetNativeCallback = unsafe extern "C" fn(
 	ctx: *const c_void,
modifiedcmds/jrsonnet-fmt/src/comments.rsdiffbeforeafterboth
--- a/cmds/jrsonnet-fmt/src/comments.rs
+++ b/cmds/jrsonnet-fmt/src/comments.rs
@@ -20,7 +20,7 @@
 		let Ok(c) = c else {
 			let mut text = c.as_ref().unwrap_err() as &str;
 			while !text.is_empty() {
-				let pos = text.find(|c| c == '\n' || c == '\t').unwrap_or(text.len());
+				let pos = text.find(['\n', '\t']).unwrap_or(text.len());
 				let sliced = &text[..pos];
 				p!(out, string(sliced.to_string()));
 				text = &text[pos..];
modifiedcmds/jrsonnet-fmt/src/tests.rsdiffbeforeafterboth
--- a/cmds/jrsonnet-fmt/src/tests.rs
+++ b/cmds/jrsonnet-fmt/src/tests.rs
@@ -75,5 +75,5 @@
             b: '',
           },
         }"
-	)))
+	)));
 }
modifiedcrates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/arglike.rs
+++ b/crates/jrsonnet-evaluator/src/function/arglike.rs
@@ -91,6 +91,7 @@
 		handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,
 	) -> Result<()>;
 	fn named_names(&self, handler: &mut dyn FnMut(&IStr));
+	fn is_empty(&self) -> bool;
 }
 
 impl ArgsLike for Vec<Val> {
@@ -117,6 +118,9 @@
 		Ok(())
 	}
 	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}
+	fn is_empty(&self) -> bool {
+		self.is_empty()
+	}
 }
 
 impl ArgsLike for ArgsDesc {
@@ -173,6 +177,10 @@
 			handler(name);
 		}
 	}
+
+	fn is_empty(&self) -> bool {
+		self.unnamed.is_empty() && self.named.is_empty()
+	}
 }
 
 impl<V: ArgLike, S> ArgsLike for HashMap<IStr, V, S> {
@@ -206,6 +214,10 @@
 			handler(name);
 		}
 	}
+
+	fn is_empty(&self) -> bool {
+		self.is_empty()
+	}
 }
 impl<V, S> OptionalContext for HashMap<IStr, V, S> where V: ArgLike + OptionalContext {}
 
@@ -235,6 +247,10 @@
 	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {
 		self.0.named_names(handler);
 	}
+
+	fn is_empty(&self) -> bool {
+		self.0.is_empty()
+	}
 }
 
 macro_rules! impl_args_like {
@@ -267,43 +283,13 @@
 				Ok(())
 			}
 			fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}
-		}
-		impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}
 
-		impl<$($gen: ArgLike,)*> ArgsLike for ($((IStr, $gen),)*) {
-			fn unnamed_len(&self) -> usize {
-				0
-			}
-			fn unnamed_iter(
-				&self,
-				_ctx: Context,
-				_tailstrict: bool,
-				_handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,
-			) -> Result<()> {
-				Ok(())
-			}
-			#[allow(non_snake_case)]
-			fn named_iter(
-				&self,
-				ctx: Context,
-				tailstrict: bool,
-				handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,
-			) -> Result<()> {
-				let ($($gen,)*) = self;
-				$(
-					handler(&$gen.0, $gen.1.evaluate_arg(ctx.clone(), tailstrict)?)?;
-				)*
-				Ok(())
-			}
-			#[allow(non_snake_case)]
-			fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {
-				let ($($gen,)*) = self;
-				$(
-					handler(&$gen.0);
-				)*
+			fn is_empty(&self) -> bool {
+				// impl_args_like only implements non-empty tuples.
+				false
 			}
 		}
-		impl<$($gen: ArgLike,)*> OptionalContext for ($((IStr, $gen),)*) where $($gen: OptionalContext),* {}
+		impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}
 	};
 	($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {
 		impl_args_like!($count; $($cur)*);
@@ -342,5 +328,8 @@
 	}
 
 	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}
+	fn is_empty(&self) -> bool {
+		true
+	}
 }
 impl OptionalContext for () {}
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -12,7 +12,10 @@
 	native::NativeDesc,
 	parse::{parse_default_function_call, parse_function_call},
 };
-use crate::{evaluate, evaluate_trivial, gc::TraceBox, tb, Context, ContextBuilder, Result, Val};
+use crate::{
+	bail, error::ErrorKind::*, evaluate, evaluate_trivial, gc::TraceBox, tb, Context,
+	ContextBuilder, Result, Thunk, Val,
+};
 
 pub mod arglike;
 pub mod builtin;
@@ -94,6 +97,8 @@
 	Id,
 	/// Plain function implemented in jsonnet.
 	Normal(Cc<FuncDesc>),
+	/// Function without arguments works just as a fancy thunk value.
+	Thunk(Thunk<Val>),
 	/// Standard library function.
 	StaticBuiltin(#[trace(skip)] &'static dyn StaticBuiltin),
 	/// User-provided function.
@@ -104,6 +109,7 @@
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 		match self {
 			Self::Id => f.debug_tuple("Id").finish(),
+			Self::Thunk(arg0) => f.debug_tuple("Thunk").field(arg0).finish(),
 			Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),
 			Self::StaticBuiltin(arg0) => {
 				f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()
@@ -146,6 +152,7 @@
 					)
 				})
 				.collect(),
+			Self::Thunk(_) => vec![],
 		}
 	}
 	/// Amount of non-default required arguments
@@ -155,6 +162,7 @@
 			Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),
 			Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default()).count(),
 			Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default()).count(),
+			Self::Thunk(_) => 0,
 		}
 	}
 	/// Function name, as defined in code.
@@ -164,6 +172,7 @@
 			Self::Normal(normal) => normal.name.clone(),
 			Self::StaticBuiltin(builtin) => builtin.name().into(),
 			Self::Builtin(builtin) => builtin.name().into(),
+			Self::Thunk(_) => "thunk".into(),
 		}
 	}
 	/// Call function using arguments evaluated in specified `call_ctx` [`Context`].
@@ -182,6 +191,12 @@
 				let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;
 				evaluate(body_ctx, &func.body)
 			}
+			Self::Thunk(thunk) => {
+				if args.is_empty() {
+					bail!(TooManyArgsFunctionHas(0, vec![],))
+				}
+				thunk.evaluate()
+			}
 			Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),
 			Self::Builtin(b) => b.call(call_ctx, loc, args),
 		}
modifiedcrates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/stdlib/format.rs
1//! faster std.format impl2#![allow(clippy::too_many_arguments)]34use jrsonnet_gcmodule::Trace;5use jrsonnet_interner::IStr;6use jrsonnet_types::ValType;7use thiserror::Error;89use crate::{10	bail,11	error::{format_found, suggest_object_fields, ErrorKind::*},12	typed::Typed,13	Error, ObjValue, Result, Val,14};1516#[derive(Debug, Clone, Error, Trace)]17pub enum FormatError {18	#[error("truncated format code")]19	TruncatedFormatCode,20	#[error("unrecognized conversion type: {0}")]21	UnrecognizedConversionType(char),2223	#[error("not enough values")]24	NotEnoughValues,2526	#[error("cannot use * width with object")]27	CannotUseStarWidthWithObject,28	#[error("mapping keys required")]29	MappingKeysRequired,30	#[error("no such format field: {0}")]31	NoSuchFormatField(IStr),3233	#[error("expected subfield <{0}> to be an object, got {1} instead")]34	SubfieldDidntYieldAnObject(IStr, ValType),35	#[error("subfield not found: <[{full}]{current}>{}", format_found(.found, "subfield"))]36	SubfieldNotFound {37		current: IStr,38		full: IStr,39		found: Box<Vec<IStr>>,40	},41}4243impl From<FormatError> for Error {44	fn from(e: FormatError) -> Self {45		Self::new(Format(e))46	}47}4849use FormatError::*;5051type ParseResult<'t, T> = std::result::Result<(T, &'t str), FormatError>;5253pub fn try_parse_mapping_key(str: &str) -> ParseResult<'_, &str> {54	if str.is_empty() {55		return Err(TruncatedFormatCode);56	}57	let bytes = str.as_bytes();58	if bytes[0] == b'(' {59		let mut i = 1;60		while i < bytes.len() {61			if bytes[i] == b')' {62				return Ok((&str[1..i], &str[i + 1..]));63			}64			i += 1;65		}66		Err(TruncatedFormatCode)67	} else {68		Ok(("", str))69	}70}7172#[cfg(test)]73pub mod tests_key {74	use super::*;7576	#[test]77	fn parse_key() {78		assert_eq!(79			try_parse_mapping_key("(hello ) world").unwrap(),80			("hello ", " world")81		);82		assert_eq!(try_parse_mapping_key("() world").unwrap(), ("", " world"));83		assert_eq!(try_parse_mapping_key(" world").unwrap(), ("", " world"));84		assert_eq!(85			try_parse_mapping_key(" () world").unwrap(),86			("", " () world")87		);88	}8990	#[test]91	#[should_panic = "TruncatedFormatCode"]92	fn parse_key_missing_start() {93		try_parse_mapping_key("").unwrap();94	}9596	#[test]97	#[should_panic = "TruncatedFormatCode"]98	fn parse_key_missing_end() {99		try_parse_mapping_key("(   ").unwrap();100	}101}102103#[allow(clippy::struct_excessive_bools)]104#[derive(Default, Debug)]105pub struct CFlags {106	pub alt: bool,107	pub zero: bool,108	pub left: bool,109	pub blank: bool,110	pub sign: bool,111}112113pub fn try_parse_cflags(str: &str) -> ParseResult<'_, CFlags> {114	if str.is_empty() {115		return Err(TruncatedFormatCode);116	}117	let bytes = str.as_bytes();118	let mut i = 0;119	let mut out = CFlags::default();120	loop {121		if bytes.len() == i {122			return Err(TruncatedFormatCode);123		}124		match bytes[i] {125			b'#' => out.alt = true,126			b'0' => out.zero = true,127			b'-' => out.left = true,128			b' ' => out.blank = true,129			b'+' => out.sign = true,130			_ => break,131		}132		i += 1;133	}134	Ok((out, &str[i..]))135}136137#[derive(Debug, PartialEq, Eq)]138pub enum Width {139	Star,140	Fixed(usize),141}142pub fn try_parse_field_width(str: &str) -> ParseResult<'_, Width> {143	if str.is_empty() {144		return Err(TruncatedFormatCode);145	}146	let bytes = str.as_bytes();147	if bytes[0] == b'*' {148		return Ok((Width::Star, &str[1..]));149	}150	let mut out: usize = 0;151	let mut digits = 0;152	while let Some(digit) = (bytes[digits] as char).to_digit(10) {153		out *= 10;154		out += digit as usize;155		digits += 1;156		if digits == bytes.len() {157			return Err(TruncatedFormatCode);158		}159	}160	Ok((Width::Fixed(out), &str[digits..]))161}162163pub fn try_parse_precision(str: &str) -> ParseResult<'_, Option<Width>> {164	if str.is_empty() {165		return Err(TruncatedFormatCode);166	}167	let bytes = str.as_bytes();168	if bytes[0] == b'.' {169		try_parse_field_width(&str[1..]).map(|(r, s)| (Some(r), s))170	} else {171		Ok((None, str))172	}173}174175// Only skips176pub fn try_parse_length_modifier(str: &str) -> ParseResult<'_, ()> {177	if str.is_empty() {178		return Err(TruncatedFormatCode);179	}180	let bytes = str.as_bytes();181	let mut idx = 0;182	while bytes[idx] == b'h' || bytes[idx] == b'l' || bytes[idx] == b'L' {183		idx += 1;184		if bytes.len() == idx {185			return Err(TruncatedFormatCode);186		}187	}188	Ok(((), &str[idx..]))189}190191#[derive(Debug, PartialEq, Eq)]192pub enum ConvTypeV {193	Decimal,194	Octal,195	Hexadecimal,196	Scientific,197	Float,198	Shorter,199	Char,200	String,201	Percent,202}203pub struct ConvType {204	v: ConvTypeV,205	caps: bool,206}207208pub fn parse_conversion_type(str: &str) -> ParseResult<'_, ConvType> {209	if str.is_empty() {210		return Err(TruncatedFormatCode);211	}212213	let code = str.as_bytes()[0];214	let v: (ConvTypeV, bool) = match code {215		b'd' | b'i' | b'u' => (ConvTypeV::Decimal, false),216		b'o' => (ConvTypeV::Octal, false),217		b'x' => (ConvTypeV::Hexadecimal, false),218		b'X' => (ConvTypeV::Hexadecimal, true),219		b'e' => (ConvTypeV::Scientific, false),220		b'E' => (ConvTypeV::Scientific, true),221		b'f' => (ConvTypeV::Float, false),222		b'F' => (ConvTypeV::Float, true),223		b'g' => (ConvTypeV::Shorter, false),224		b'G' => (ConvTypeV::Shorter, true),225		b'c' => (ConvTypeV::Char, false),226		b's' => (ConvTypeV::String, false),227		b'%' => (ConvTypeV::Percent, false),228		c => return Err(UnrecognizedConversionType(c as char)),229	};230231	Ok((ConvType { v: v.0, caps: v.1 }, &str[1..]))232}233234#[derive(Debug)]235pub struct Code<'s> {236	mkey: &'s str,237	cflags: CFlags,238	width: Width,239	precision: Option<Width>,240	convtype: ConvTypeV,241	caps: bool,242}243pub fn parse_code(str: &str) -> ParseResult<'_, Code<'_>> {244	if str.is_empty() {245		return Err(TruncatedFormatCode);246	}247	let (mkey, str) = try_parse_mapping_key(str)?;248	let (cflags, str) = try_parse_cflags(str)?;249	let (width, str) = try_parse_field_width(str)?;250	let (precision, str) = try_parse_precision(str)?;251	let ((), str) = try_parse_length_modifier(str)?;252	let (convtype, str) = parse_conversion_type(str)?;253254	Ok((255		Code {256			mkey,257			cflags,258			width,259			precision,260			convtype: convtype.v,261			caps: convtype.caps,262		},263		str,264	))265}266267#[derive(Debug)]268pub enum Element<'s> {269	String(&'s str),270	Code(Code<'s>),271}272pub fn parse_codes(mut str: &str) -> Result<Vec<Element<'_>>> {273	let mut bytes = str.as_bytes();274	let mut out = vec![];275	let mut offset = 0;276277	loop {278		while offset != bytes.len() && bytes[offset] != b'%' {279			offset += 1;280		}281		if offset != 0 {282			out.push(Element::String(&str[0..offset]));283		}284		if offset == bytes.len() {285			return Ok(out);286		}287		str = &str[offset + 1..];288		let code;289		(code, str) = parse_code(str)?;290		bytes = str.as_bytes();291		offset = 0;292293		out.push(Element::Code(code));294	}295}296297const NUMBERS: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";298299#[inline]300pub fn render_integer(301	out: &mut String,302	iv: f64,303	padding: usize,304	precision: usize,305	blank: bool,306	sign: bool,307	radix: i64,308	prefix: &str,309	caps: bool,310) {311	let radix = radix as f64;312	let iv = iv.floor();313	// Digit char indexes in reverse order, i.e314	// for radix = 16 and n = 12f: [15, 2, 1]315	let digits = if iv == 0.0 {316		vec![0u8]317	} else {318		let mut v = iv.abs();319		let mut nums = Vec::with_capacity(1);320		while v != 0.0 {321			nums.push((v % radix) as u8);322			v = (v / radix).floor();323		}324		nums325	};326	let neg = iv < 0.0;327	#[allow(clippy::bool_to_int_with_if)]328	let zp = padding.saturating_sub(if neg || blank || sign { 1 } else { 0 });329	let zp2 = zp330		.max(precision)331		.saturating_sub(prefix.len() + digits.len());332333	if neg {334		out.push('-');335	} else if sign {336		out.push('+');337	} else if blank {338		out.push(' ');339	}340341	out.reserve(zp2);342	for _ in 0..zp2 {343		out.push('0');344	}345	out.push_str(prefix);346347	for digit in digits.into_iter().rev() {348		let ch = NUMBERS[digit as usize] as char;349		out.push(if caps { ch.to_ascii_uppercase() } else { ch });350	}351}352353pub fn render_decimal(354	out: &mut String,355	iv: f64,356	padding: usize,357	precision: usize,358	blank: bool,359	sign: bool,360) {361	render_integer(out, iv, padding, precision, blank, sign, 10, "", false);362}363pub fn render_octal(364	out: &mut String,365	iv: f64,366	padding: usize,367	precision: usize,368	alt: bool,369	blank: bool,370	sign: bool,371) {372	render_integer(373		out,374		iv,375		padding,376		precision,377		blank,378		sign,379		8,380		if alt && iv != 0.0 { "0" } else { "" },381		false,382	);383}384385#[allow(clippy::fn_params_excessive_bools)]386pub fn render_hexadecimal(387	out: &mut String,388	iv: f64,389	padding: usize,390	precision: usize,391	alt: bool,392	blank: bool,393	sign: bool,394	caps: bool,395) {396	render_integer(397		out,398		iv,399		padding,400		precision,401		blank,402		sign,403		16,404		match (alt, caps) {405			(true, true) => "0X",406			(true, false) => "0x",407			(false, _) => "",408		},409		caps,410	);411}412413#[allow(clippy::fn_params_excessive_bools)]414pub fn render_float(415	out: &mut String,416	n: f64,417	mut padding: usize,418	precision: usize,419	blank: bool,420	sign: bool,421	ensure_pt: bool,422	trailing: bool,423) {424	#[allow(clippy::bool_to_int_with_if)]425	let dot_size = if precision == 0 && !ensure_pt { 0 } else { 1 };426	padding = padding.saturating_sub(dot_size + precision);427	render_decimal(out, n.floor(), padding, 0, blank, sign);428	if precision == 0 {429		if ensure_pt {430			out.push('.');431		}432		return;433	}434	let frac = n435		.fract()436		.mul_add(10.0_f64.powf(precision as f64), 0.5)437		.floor();438	if trailing || frac > 0.0 {439		out.push('.');440		let mut frac_str = String::new();441		render_decimal(&mut frac_str, frac, precision, 0, false, false);442		let mut trim = frac_str.len();443		if !trailing {444			for b in frac_str.as_bytes().iter().rev() {445				if *b == b'0' {446					trim -= 1;447				} else {448					break;449				}450			}451		}452		out.push_str(&frac_str[..trim]);453	} else if ensure_pt {454		out.push('.');455	}456}457458#[allow(clippy::fn_params_excessive_bools)]459pub fn render_float_sci(460	out: &mut String,461	n: f64,462	mut padding: usize,463	precision: usize,464	blank: bool,465	sign: bool,466	ensure_pt: bool,467	trailing: bool,468	caps: bool,469) {470	let exponent = n.log10().floor();471	let mantissa = if exponent as i16 == -324 {472		n * 10.0 / 10.0_f64.powf(exponent + 1.0)473	} else {474		n / 10.0_f64.powf(exponent)475	};476	let mut exponent_str = String::new();477	render_decimal(&mut exponent_str, exponent, 3, 0, false, true);478479	// +1 for e480	padding = padding.saturating_sub(exponent_str.len() + 1);481482	render_float(483		out, mantissa, padding, precision, blank, sign, ensure_pt, trailing,484	);485	out.push(if caps { 'E' } else { 'e' });486	out.push_str(&exponent_str);487}488489#[allow(clippy::too_many_lines)]490pub fn format_code(491	out: &mut String,492	value: &Val,493	code: &Code<'_>,494	width: usize,495	precision: Option<usize>,496) -> Result<()> {497	let clfags = &code.cflags;498	let (fpprec, iprec) = precision.map_or((6, 0), |v| (v, v));499	let padding = if clfags.zero && !clfags.left {500		width501	} else {502		0503	};504505	// TODO: If left padded, can optimize by writing directly to out506	let mut tmp_out = String::new();507508	match code.convtype {509		ConvTypeV::String => tmp_out.push_str(&value.clone().to_string()?),510		ConvTypeV::Decimal => {511			let value = f64::from_untyped(value.clone())?;512			render_decimal(513				&mut tmp_out,514				value,515				padding,516				iprec,517				clfags.blank,518				clfags.sign,519			);520		}521		ConvTypeV::Octal => {522			let value = f64::from_untyped(value.clone())?;523			render_octal(524				&mut tmp_out,525				value,526				padding,527				iprec,528				clfags.alt,529				clfags.blank,530				clfags.sign,531			);532		}533		ConvTypeV::Hexadecimal => {534			let value = f64::from_untyped(value.clone())?;535			render_hexadecimal(536				&mut tmp_out,537				value,538				padding,539				iprec,540				clfags.alt,541				clfags.blank,542				clfags.sign,543				code.caps,544			);545		}546		ConvTypeV::Scientific => {547			let value = f64::from_untyped(value.clone())?;548			render_float_sci(549				&mut tmp_out,550				value,551				padding,552				fpprec,553				clfags.blank,554				clfags.sign,555				clfags.alt,556				true,557				code.caps,558			);559		}560		ConvTypeV::Float => {561			let value = f64::from_untyped(value.clone())?;562			render_float(563				&mut tmp_out,564				value,565				padding,566				fpprec,567				clfags.blank,568				clfags.sign,569				clfags.alt,570				true,571			);572		}573		ConvTypeV::Shorter => {574			let value = f64::from_untyped(value.clone())?;575			let exponent = if value == 0.0 {576				0.0577			} else {578				value.abs().log10().floor()579			};580			if exponent < -4.0 || exponent >= fpprec as f64 {581				render_float_sci(582					&mut tmp_out,583					value,584					padding,585					fpprec - 1,586					clfags.blank,587					clfags.sign,588					clfags.alt,589					clfags.alt,590					code.caps,591				);592			} else {593				let digits_before_pt = 1.max(exponent as usize + 1);594				render_float(595					&mut tmp_out,596					value,597					padding,598					fpprec - digits_before_pt,599					clfags.blank,600					clfags.sign,601					clfags.alt,602					clfags.alt,603				);604			}605		}606		ConvTypeV::Char => match value.clone() {607			Val::Num(n) => {608				let n = n.get();609				tmp_out.push(610					std::char::from_u32(n as u32)611						.ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?,612				);613			}614			Val::Str(s) => {615				let s = s.into_flat();616				if s.chars().count() != 1 {617					bail!("%c expected 1 char string, got {}", s.chars().count());618				}619				tmp_out.push_str(&s);620			}621			_ => {622				bail!(TypeMismatch(623					"%c requires number/string",624					vec![ValType::Num, ValType::Str],625					value.value_type(),626				));627			}628		},629		ConvTypeV::Percent => tmp_out.push('%'),630	};631632	let padding = width.saturating_sub(tmp_out.len());633634	if !clfags.left {635		for _ in 0..padding {636			out.push(' ');637		}638	}639	out.push_str(&tmp_out);640	if clfags.left {641		for _ in 0..padding {642			out.push(' ');643		}644	}645646	Ok(())647}648649pub fn format_arr(str: &str, mut values: &[Val]) -> Result<String> {650	let codes = parse_codes(str)?;651	let mut out = String::new();652	let value_count = values.len();653654	for code in codes {655		match code {656			Element::String(s) => {657				out.push_str(s);658			}659			Element::Code(c) => {660				let width = match c.width {661					Width::Star => {662						if values.is_empty() {663							bail!(NotEnoughValues);664						}665						let value = &values[0];666						values = &values[1..];667						usize::from_untyped(value.clone())?668					}669					Width::Fixed(n) => n,670				};671				let precision = match c.precision {672					Some(Width::Star) => {673						if values.is_empty() {674							bail!(NotEnoughValues);675						}676						let value = &values[0];677						values = &values[1..];678						Some(usize::from_untyped(value.clone())?)679					}680					Some(Width::Fixed(n)) => Some(n),681					None => None,682				};683684				// %% should not consume a value685				let value = if c.convtype == ConvTypeV::Percent {686					&Val::Null687				} else {688					if values.is_empty() {689						bail!(NotEnoughValues);690					}691					let value = &values[0];692					values = &values[1..];693					value694				};695696				format_code(&mut out, value, &c, width, precision)?;697			}698		}699	}700701	if !values.is_empty() {702		bail!(703			"too many values to format, expected {value_count}, got {}",704			value_count + values.len()705		)706	}707708	Ok(out)709}710711fn get_dotted_field(obj: ObjValue, field: &str) -> Result<Val> {712	let mut current = Val::Obj(obj);713	let mut name_offset = 0;714	for component in field.split('.') {715		let end_offset = name_offset + component.len();716		current = if let Val::Obj(obj) = current {717			if let Some(value) = obj.get(component.into())? {718				value719			} else {720				let current = &field[name_offset..end_offset];721				let full = &field[..name_offset];722				let found = Box::new(suggest_object_fields(&obj, current.into()));723				bail!(SubfieldNotFound {724					current: current.into(),725					full: full.into(),726					found,727				})728			}729		} else {730			// No underflow may happen, initially we always start with an object731			let subfield = &field[..name_offset - 1];732			bail!(SubfieldDidntYieldAnObject(733				subfield.into(),734				current.value_type()735			));736		};737		name_offset = end_offset + 1;738	}739	Ok(current)740}741742pub fn format_obj(str: &str, values: &ObjValue) -> Result<String> {743	let codes = parse_codes(str)?;744	let mut out = String::new();745746	for code in codes {747		match code {748			Element::String(s) => {749				out.push_str(s);750			}751			Element::Code(c) => {752				// TODO: Operate on ref753				let f: IStr = c.mkey.into();754				let width = match c.width {755					Width::Star => {756						bail!(CannotUseStarWidthWithObject);757					}758					Width::Fixed(n) => n,759				};760				let precision = match c.precision {761					Some(Width::Star) => {762						bail!(CannotUseStarWidthWithObject);763					}764					Some(Width::Fixed(n)) => Some(n),765					None => None,766				};767768				let value = if c.convtype == ConvTypeV::Percent {769					Val::Null770				} else {771					if f.is_empty() {772						bail!(MappingKeysRequired);773					}774					if let Some(v) = values.get(f.clone())? {775						v776					} else {777						get_dotted_field(values.clone(), &f)?778					}779				};780781				format_code(&mut out, &value, &c, width, precision)?;782			}783		}784	}785786	Ok(out)787}788789#[cfg(test)]790pub mod test_format {791	use super::*;792	use crate::val::NumValue;793794	#[test]795	fn parse() {796		assert_eq!(797			parse_codes(798				"How much error budget is left looking at our %.3f%% availability gurantees?"799			)800			.unwrap()801			.len(),802			4803		);804	}805806	fn num(v: f64) -> Val {807		Val::Num(NumValue::new(v).expect("finite"))808	}809810	#[test]811	fn octals() {812		assert_eq!(format_arr("%#o", &[num(8.0)]).unwrap(), "010");813		assert_eq!(format_arr("%#4o", &[num(8.0)]).unwrap(), " 010");814		assert_eq!(format_arr("%4o", &[num(8.0)]).unwrap(), "  10");815		assert_eq!(format_arr("%04o", &[num(8.0)]).unwrap(), "0010");816		assert_eq!(format_arr("%+4o", &[num(8.0)]).unwrap(), " +10");817		assert_eq!(format_arr("%+04o", &[num(8.0)]).unwrap(), "+010");818		assert_eq!(format_arr("%-4o", &[num(8.0)]).unwrap(), "10  ");819		assert_eq!(format_arr("%+-4o", &[num(8.0)]).unwrap(), "+10 ");820		assert_eq!(format_arr("%+-04o", &[num(8.0)]).unwrap(), "+10 ");821	}822823	#[test]824	fn percent_doesnt_consumes_values() {825		assert_eq!(826			format_arr(827				"How much error budget is left looking at our %.3f%% availability gurantees?",828				&[num(4.0)]829			)830			.unwrap(),831			"How much error budget is left looking at our 4.000% availability gurantees?"832		);833	}834}
after · crates/jrsonnet-evaluator/src/stdlib/format.rs
1//! faster std.format impl2#![allow(clippy::too_many_arguments)]34use jrsonnet_gcmodule::Trace;5use jrsonnet_interner::IStr;6use jrsonnet_types::ValType;7use thiserror::Error;89use crate::{10	bail,11	error::{format_found, suggest_object_fields, ErrorKind::*},12	typed::Typed,13	Error, ObjValue, Result, Val,14};1516#[derive(Debug, Clone, Error, Trace)]17pub enum FormatError {18	#[error("truncated format code")]19	TruncatedFormatCode,20	#[error("unrecognized conversion type: {0}")]21	UnrecognizedConversionType(char),2223	#[error("not enough values")]24	NotEnoughValues,2526	#[error("cannot use * width with object")]27	CannotUseStarWidthWithObject,28	#[error("mapping keys required")]29	MappingKeysRequired,30	#[error("no such format field: {0}")]31	NoSuchFormatField(IStr),3233	#[error("expected subfield <{0}> to be an object, got {1} instead")]34	SubfieldDidntYieldAnObject(IStr, ValType),35	#[error("subfield not found: <[{full}]{current}>{}", format_found(.found, "subfield"))]36	SubfieldNotFound {37		current: IStr,38		full: IStr,39		found: Box<Vec<IStr>>,40	},41}4243impl From<FormatError> for Error {44	fn from(e: FormatError) -> Self {45		Self::new(Format(e))46	}47}4849use FormatError::*;5051type ParseResult<'t, T> = std::result::Result<(T, &'t str), FormatError>;5253pub fn try_parse_mapping_key(str: &str) -> ParseResult<'_, &str> {54	if str.is_empty() {55		return Err(TruncatedFormatCode);56	}57	let bytes = str.as_bytes();58	if bytes[0] == b'(' {59		let mut i = 1;60		while i < bytes.len() {61			if bytes[i] == b')' {62				return Ok((&str[1..i], &str[i + 1..]));63			}64			i += 1;65		}66		Err(TruncatedFormatCode)67	} else {68		Ok(("", str))69	}70}7172#[cfg(test)]73pub mod tests_key {74	use super::*;7576	#[test]77	fn parse_key() {78		assert_eq!(79			try_parse_mapping_key("(hello ) world").unwrap(),80			("hello ", " world")81		);82		assert_eq!(try_parse_mapping_key("() world").unwrap(), ("", " world"));83		assert_eq!(try_parse_mapping_key(" world").unwrap(), ("", " world"));84		assert_eq!(85			try_parse_mapping_key(" () world").unwrap(),86			("", " () world")87		);88	}8990	#[test]91	#[should_panic = "TruncatedFormatCode"]92	fn parse_key_missing_start() {93		try_parse_mapping_key("").unwrap();94	}9596	#[test]97	#[should_panic = "TruncatedFormatCode"]98	fn parse_key_missing_end() {99		try_parse_mapping_key("(   ").unwrap();100	}101}102103#[allow(clippy::struct_excessive_bools)]104#[derive(Default, Debug)]105pub struct CFlags {106	pub alt: bool,107	pub zero: bool,108	pub left: bool,109	pub blank: bool,110	pub sign: bool,111}112113pub fn try_parse_cflags(str: &str) -> ParseResult<'_, CFlags> {114	if str.is_empty() {115		return Err(TruncatedFormatCode);116	}117	let bytes = str.as_bytes();118	let mut i = 0;119	let mut out = CFlags::default();120	loop {121		if bytes.len() == i {122			return Err(TruncatedFormatCode);123		}124		match bytes[i] {125			b'#' => out.alt = true,126			b'0' => out.zero = true,127			b'-' => out.left = true,128			b' ' => out.blank = true,129			b'+' => out.sign = true,130			_ => break,131		}132		i += 1;133	}134	Ok((out, &str[i..]))135}136137#[derive(Debug, PartialEq, Eq)]138pub enum Width {139	Star,140	Fixed(usize),141}142pub fn try_parse_field_width(str: &str) -> ParseResult<'_, Width> {143	if str.is_empty() {144		return Err(TruncatedFormatCode);145	}146	let bytes = str.as_bytes();147	if bytes[0] == b'*' {148		return Ok((Width::Star, &str[1..]));149	}150	let mut out: usize = 0;151	let mut digits = 0;152	while let Some(digit) = (bytes[digits] as char).to_digit(10) {153		out *= 10;154		out += digit as usize;155		digits += 1;156		if digits == bytes.len() {157			return Err(TruncatedFormatCode);158		}159	}160	Ok((Width::Fixed(out), &str[digits..]))161}162163pub fn try_parse_precision(str: &str) -> ParseResult<'_, Option<Width>> {164	if str.is_empty() {165		return Err(TruncatedFormatCode);166	}167	let bytes = str.as_bytes();168	if bytes[0] == b'.' {169		try_parse_field_width(&str[1..]).map(|(r, s)| (Some(r), s))170	} else {171		Ok((None, str))172	}173}174175// Only skips176pub fn try_parse_length_modifier(str: &str) -> ParseResult<'_, ()> {177	if str.is_empty() {178		return Err(TruncatedFormatCode);179	}180	let bytes = str.as_bytes();181	let mut idx = 0;182	while bytes[idx] == b'h' || bytes[idx] == b'l' || bytes[idx] == b'L' {183		idx += 1;184		if bytes.len() == idx {185			return Err(TruncatedFormatCode);186		}187	}188	Ok(((), &str[idx..]))189}190191#[derive(Debug, PartialEq, Eq)]192pub enum ConvTypeV {193	Decimal,194	Octal,195	Hexadecimal,196	Scientific,197	Float,198	Shorter,199	Char,200	String,201	Percent,202}203pub struct ConvType {204	v: ConvTypeV,205	caps: bool,206}207208pub fn parse_conversion_type(str: &str) -> ParseResult<'_, ConvType> {209	if str.is_empty() {210		return Err(TruncatedFormatCode);211	}212213	let code = str.as_bytes()[0];214	let v: (ConvTypeV, bool) = match code {215		b'd' | b'i' | b'u' => (ConvTypeV::Decimal, false),216		b'o' => (ConvTypeV::Octal, false),217		b'x' => (ConvTypeV::Hexadecimal, false),218		b'X' => (ConvTypeV::Hexadecimal, true),219		b'e' => (ConvTypeV::Scientific, false),220		b'E' => (ConvTypeV::Scientific, true),221		b'f' => (ConvTypeV::Float, false),222		b'F' => (ConvTypeV::Float, true),223		b'g' => (ConvTypeV::Shorter, false),224		b'G' => (ConvTypeV::Shorter, true),225		b'c' => (ConvTypeV::Char, false),226		b's' => (ConvTypeV::String, false),227		b'%' => (ConvTypeV::Percent, false),228		c => return Err(UnrecognizedConversionType(c as char)),229	};230231	Ok((ConvType { v: v.0, caps: v.1 }, &str[1..]))232}233234#[derive(Debug)]235pub struct Code<'s> {236	mkey: &'s str,237	cflags: CFlags,238	width: Width,239	precision: Option<Width>,240	convtype: ConvTypeV,241	caps: bool,242}243pub fn parse_code(str: &str) -> ParseResult<'_, Code<'_>> {244	if str.is_empty() {245		return Err(TruncatedFormatCode);246	}247	let (mkey, str) = try_parse_mapping_key(str)?;248	let (cflags, str) = try_parse_cflags(str)?;249	let (width, str) = try_parse_field_width(str)?;250	let (precision, str) = try_parse_precision(str)?;251	let ((), str) = try_parse_length_modifier(str)?;252	let (convtype, str) = parse_conversion_type(str)?;253254	Ok((255		Code {256			mkey,257			cflags,258			width,259			precision,260			convtype: convtype.v,261			caps: convtype.caps,262		},263		str,264	))265}266267#[derive(Debug)]268pub enum Element<'s> {269	String(&'s str),270	Code(Code<'s>),271}272pub fn parse_codes(mut str: &str) -> Result<Vec<Element<'_>>> {273	let mut bytes = str.as_bytes();274	let mut out = vec![];275	let mut offset = 0;276277	loop {278		while offset != bytes.len() && bytes[offset] != b'%' {279			offset += 1;280		}281		if offset != 0 {282			out.push(Element::String(&str[0..offset]));283		}284		if offset == bytes.len() {285			return Ok(out);286		}287		str = &str[offset + 1..];288		let code;289		(code, str) = parse_code(str)?;290		bytes = str.as_bytes();291		offset = 0;292293		out.push(Element::Code(code));294	}295}296297const NUMBERS: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";298299#[inline]300pub fn render_integer(301	out: &mut String,302	iv: f64,303	padding: usize,304	precision: usize,305	blank: bool,306	sign: bool,307	radix: i64,308	prefix: &str,309	caps: bool,310) {311	let iv = iv.floor() as i64;312	// Digit char indexes in reverse order, i.e313	// for radix = 16 and n = 12f: [15, 2, 1]314	let digits = if iv == 0 {315		vec![0u8]316	} else {317		let mut v = iv.abs();318		let mut nums = Vec::with_capacity(1);319		while v != 0 {320			nums.push((v % radix) as u8);321			v /= radix;322		}323		nums324	};325	let neg = iv < 0;326	#[allow(clippy::bool_to_int_with_if)]327	let zp = padding.saturating_sub(if neg || blank || sign { 1 } else { 0 });328	let zp2 = zp329		.max(precision)330		.saturating_sub(prefix.len() + digits.len());331332	if neg {333		out.push('-');334	} else if sign {335		out.push('+');336	} else if blank {337		out.push(' ');338	}339340	out.reserve(zp2);341	for _ in 0..zp2 {342		out.push('0');343	}344	out.push_str(prefix);345346	for digit in digits.into_iter().rev() {347		let ch = NUMBERS[digit as usize] as char;348		out.push(if caps { ch.to_ascii_uppercase() } else { ch });349	}350}351352pub fn render_decimal(353	out: &mut String,354	iv: f64,355	padding: usize,356	precision: usize,357	blank: bool,358	sign: bool,359) {360	render_integer(out, iv, padding, precision, blank, sign, 10, "", false);361}362pub fn render_octal(363	out: &mut String,364	iv: f64,365	padding: usize,366	precision: usize,367	alt: bool,368	blank: bool,369	sign: bool,370) {371	render_integer(372		out,373		iv,374		padding,375		precision,376		blank,377		sign,378		8,379		if alt && iv != 0.0 { "0" } else { "" },380		false,381	);382}383384#[allow(clippy::fn_params_excessive_bools)]385pub fn render_hexadecimal(386	out: &mut String,387	iv: f64,388	padding: usize,389	precision: usize,390	alt: bool,391	blank: bool,392	sign: bool,393	caps: bool,394) {395	render_integer(396		out,397		iv,398		padding,399		precision,400		blank,401		sign,402		16,403		match (alt, caps) {404			(true, true) => "0X",405			(true, false) => "0x",406			(false, _) => "",407		},408		caps,409	);410}411412#[allow(clippy::fn_params_excessive_bools)]413pub fn render_float(414	out: &mut String,415	n: f64,416	mut padding: usize,417	precision: usize,418	blank: bool,419	sign: bool,420	ensure_pt: bool,421	trailing: bool,422) {423	#[allow(clippy::bool_to_int_with_if)]424	let dot_size = if precision == 0 && !ensure_pt { 0 } else { 1 };425	padding = padding.saturating_sub(dot_size + precision);426	render_decimal(out, n.floor(), padding, 0, blank, sign);427	if precision == 0 {428		if ensure_pt {429			out.push('.');430		}431		return;432	}433	let frac = n434		.fract()435		.mul_add(10.0_f64.powf(precision as f64), 0.5)436		.floor();437	if trailing || frac > 0.0 {438		out.push('.');439		let mut frac_str = String::new();440		render_decimal(&mut frac_str, frac, precision, 0, false, false);441		let mut trim = frac_str.len();442		if !trailing {443			for b in frac_str.as_bytes().iter().rev() {444				if *b == b'0' {445					trim -= 1;446				} else {447					break;448				}449			}450		}451		out.push_str(&frac_str[..trim]);452	} else if ensure_pt {453		out.push('.');454	}455}456457#[allow(clippy::fn_params_excessive_bools)]458pub fn render_float_sci(459	out: &mut String,460	n: f64,461	mut padding: usize,462	precision: usize,463	blank: bool,464	sign: bool,465	ensure_pt: bool,466	trailing: bool,467	caps: bool,468) {469	let exponent = n.log10().floor();470	let mantissa = if exponent as i16 == -324 {471		n * 10.0 / 10.0_f64.powf(exponent + 1.0)472	} else {473		n / 10.0_f64.powf(exponent)474	};475	let mut exponent_str = String::new();476	render_decimal(&mut exponent_str, exponent, 3, 0, false, true);477478	// +1 for e479	padding = padding.saturating_sub(exponent_str.len() + 1);480481	render_float(482		out, mantissa, padding, precision, blank, sign, ensure_pt, trailing,483	);484	out.push(if caps { 'E' } else { 'e' });485	out.push_str(&exponent_str);486}487488#[allow(clippy::too_many_lines)]489pub fn format_code(490	out: &mut String,491	value: &Val,492	code: &Code<'_>,493	width: usize,494	precision: Option<usize>,495) -> Result<()> {496	let clfags = &code.cflags;497	let (fpprec, iprec) = precision.map_or((6, 0), |v| (v, v));498	let padding = if clfags.zero && !clfags.left {499		width500	} else {501		0502	};503504	// TODO: If left padded, can optimize by writing directly to out505	let mut tmp_out = String::new();506507	match code.convtype {508		ConvTypeV::String => tmp_out.push_str(&value.clone().to_string()?),509		ConvTypeV::Decimal => {510			let value = f64::from_untyped(value.clone())?;511			render_decimal(512				&mut tmp_out,513				value,514				padding,515				iprec,516				clfags.blank,517				clfags.sign,518			);519		}520		ConvTypeV::Octal => {521			let value = f64::from_untyped(value.clone())?;522			render_octal(523				&mut tmp_out,524				value,525				padding,526				iprec,527				clfags.alt,528				clfags.blank,529				clfags.sign,530			);531		}532		ConvTypeV::Hexadecimal => {533			let value = f64::from_untyped(value.clone())?;534			render_hexadecimal(535				&mut tmp_out,536				value,537				padding,538				iprec,539				clfags.alt,540				clfags.blank,541				clfags.sign,542				code.caps,543			);544		}545		ConvTypeV::Scientific => {546			let value = f64::from_untyped(value.clone())?;547			render_float_sci(548				&mut tmp_out,549				value,550				padding,551				fpprec,552				clfags.blank,553				clfags.sign,554				clfags.alt,555				true,556				code.caps,557			);558		}559		ConvTypeV::Float => {560			let value = f64::from_untyped(value.clone())?;561			render_float(562				&mut tmp_out,563				value,564				padding,565				fpprec,566				clfags.blank,567				clfags.sign,568				clfags.alt,569				true,570			);571		}572		ConvTypeV::Shorter => {573			let value = f64::from_untyped(value.clone())?;574			let exponent = if value == 0.0 {575				0.0576			} else {577				value.abs().log10().floor()578			};579			if exponent < -4.0 || exponent >= fpprec as f64 {580				render_float_sci(581					&mut tmp_out,582					value,583					padding,584					fpprec - 1,585					clfags.blank,586					clfags.sign,587					clfags.alt,588					clfags.alt,589					code.caps,590				);591			} else {592				let digits_before_pt = 1.max(exponent as usize + 1);593				render_float(594					&mut tmp_out,595					value,596					padding,597					fpprec - digits_before_pt,598					clfags.blank,599					clfags.sign,600					clfags.alt,601					clfags.alt,602				);603			}604		}605		ConvTypeV::Char => match value.clone() {606			Val::Num(n) => {607				let n = n.get();608				tmp_out.push(609					std::char::from_u32(n as u32)610						.ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?,611				);612			}613			Val::Str(s) => {614				let s = s.into_flat();615				if s.chars().count() != 1 {616					bail!("%c expected 1 char string, got {}", s.chars().count());617				}618				tmp_out.push_str(&s);619			}620			_ => {621				bail!(TypeMismatch(622					"%c requires number/string",623					vec![ValType::Num, ValType::Str],624					value.value_type(),625				));626			}627		},628		ConvTypeV::Percent => tmp_out.push('%'),629	};630631	let padding = width.saturating_sub(tmp_out.len());632633	if !clfags.left {634		for _ in 0..padding {635			out.push(' ');636		}637	}638	out.push_str(&tmp_out);639	if clfags.left {640		for _ in 0..padding {641			out.push(' ');642		}643	}644645	Ok(())646}647648pub fn format_arr(str: &str, mut values: &[Val]) -> Result<String> {649	let codes = parse_codes(str)?;650	let mut out = String::new();651	let value_count = values.len();652653	for code in codes {654		match code {655			Element::String(s) => {656				out.push_str(s);657			}658			Element::Code(c) => {659				let width = match c.width {660					Width::Star => {661						if values.is_empty() {662							bail!(NotEnoughValues);663						}664						let value = &values[0];665						values = &values[1..];666						usize::from_untyped(value.clone())?667					}668					Width::Fixed(n) => n,669				};670				let precision = match c.precision {671					Some(Width::Star) => {672						if values.is_empty() {673							bail!(NotEnoughValues);674						}675						let value = &values[0];676						values = &values[1..];677						Some(usize::from_untyped(value.clone())?)678					}679					Some(Width::Fixed(n)) => Some(n),680					None => None,681				};682683				// %% should not consume a value684				let value = if c.convtype == ConvTypeV::Percent {685					&Val::Null686				} else {687					if values.is_empty() {688						bail!(NotEnoughValues);689					}690					let value = &values[0];691					values = &values[1..];692					value693				};694695				format_code(&mut out, value, &c, width, precision)?;696			}697		}698	}699700	if !values.is_empty() {701		bail!(702			"too many values to format, expected {value_count}, got {}",703			value_count + values.len()704		)705	}706707	Ok(out)708}709710fn get_dotted_field(obj: ObjValue, field: &str) -> Result<Val> {711	let mut current = Val::Obj(obj);712	let mut name_offset = 0;713	for component in field.split('.') {714		let end_offset = name_offset + component.len();715		current = if let Val::Obj(obj) = current {716			if let Some(value) = obj.get(component.into())? {717				value718			} else {719				let current = &field[name_offset..end_offset];720				let full = &field[..name_offset];721				let found = Box::new(suggest_object_fields(&obj, current.into()));722				bail!(SubfieldNotFound {723					current: current.into(),724					full: full.into(),725					found,726				})727			}728		} else {729			// No underflow may happen, initially we always start with an object730			let subfield = &field[..name_offset - 1];731			bail!(SubfieldDidntYieldAnObject(732				subfield.into(),733				current.value_type()734			));735		};736		name_offset = end_offset + 1;737	}738	Ok(current)739}740741pub fn format_obj(str: &str, values: &ObjValue) -> Result<String> {742	let codes = parse_codes(str)?;743	let mut out = String::new();744745	for code in codes {746		match code {747			Element::String(s) => {748				out.push_str(s);749			}750			Element::Code(c) => {751				// TODO: Operate on ref752				let f: IStr = c.mkey.into();753				let width = match c.width {754					Width::Star => {755						bail!(CannotUseStarWidthWithObject);756					}757					Width::Fixed(n) => n,758				};759				let precision = match c.precision {760					Some(Width::Star) => {761						bail!(CannotUseStarWidthWithObject);762					}763					Some(Width::Fixed(n)) => Some(n),764					None => None,765				};766767				let value = if c.convtype == ConvTypeV::Percent {768					Val::Null769				} else {770					if f.is_empty() {771						bail!(MappingKeysRequired);772					}773					if let Some(v) = values.get(f.clone())? {774						v775					} else {776						get_dotted_field(values.clone(), &f)?777					}778				};779780				format_code(&mut out, &value, &c, width, precision)?;781			}782		}783	}784785	Ok(out)786}787788#[cfg(test)]789pub mod test_format {790	use super::*;791	use crate::val::NumValue;792793	#[test]794	fn parse() {795		assert_eq!(796			parse_codes(797				"How much error budget is left looking at our %.3f%% availability gurantees?"798			)799			.unwrap()800			.len(),801			4802		);803	}804805	fn num(v: f64) -> Val {806		Val::Num(NumValue::new(v).expect("finite"))807	}808809	#[test]810	fn octals() {811		assert_eq!(format_arr("%#o", &[num(8.0)]).unwrap(), "010");812		assert_eq!(format_arr("%#4o", &[num(8.0)]).unwrap(), " 010");813		assert_eq!(format_arr("%4o", &[num(8.0)]).unwrap(), "  10");814		assert_eq!(format_arr("%04o", &[num(8.0)]).unwrap(), "0010");815		assert_eq!(format_arr("%+4o", &[num(8.0)]).unwrap(), " +10");816		assert_eq!(format_arr("%+04o", &[num(8.0)]).unwrap(), "+010");817		assert_eq!(format_arr("%-4o", &[num(8.0)]).unwrap(), "10  ");818		assert_eq!(format_arr("%+-4o", &[num(8.0)]).unwrap(), "+10 ");819		assert_eq!(format_arr("%+-04o", &[num(8.0)]).unwrap(), "+10 ");820	}821822	#[test]823	fn percent_doesnt_consumes_values() {824		assert_eq!(825			format_arr(826				"How much error budget is left looking at our %.3f%% availability gurantees?",827				&[num(4.0)]828			)829			.unwrap(),830			"How much error budget is left looking at our 4.000% availability gurantees?"831		);832	}833}
modifiedcrates/jrsonnet-rowan-parser/src/ast.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/ast.rs
+++ b/crates/jrsonnet-rowan-parser/src/ast.rs
@@ -16,12 +16,14 @@
 		Self: Sized;
 
 	fn syntax(&self) -> &SyntaxNode;
+	#[must_use]
 	fn clone_for_update(&self) -> Self
 	where
 		Self: Sized,
 	{
 		Self::cast(self.syntax().clone_for_update()).unwrap()
 	}
+	#[must_use]
 	fn clone_subtree(&self) -> Self
 	where
 		Self: Sized,
@@ -70,6 +72,8 @@
 }
 
 pub mod support {
+	use rowan::NodeOrToken;
+
 	use super::{AstChildren, AstNode, AstToken, SyntaxKind, SyntaxNode, SyntaxToken};
 
 	pub fn child<N: AstNode>(parent: &SyntaxNode) -> Option<N> {
@@ -89,7 +93,7 @@
 	pub fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
 		parent
 			.children_with_tokens()
-			.filter_map(|it| it.into_token())
+			.filter_map(NodeOrToken::into_token)
 			.find(|it| it.kind() == kind)
 	}
 }
modifiedcrates/jrsonnet-rowan-parser/src/event.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/event.rs
+++ b/crates/jrsonnet-rowan-parser/src/event.rs
@@ -26,7 +26,7 @@
 	// VirtualToken { kind: SyntaxKind },
 	/// Position of finished node
 	Finish {
-		/// Same as forward_parent of Start, but for wrapping
+		/// Same as `forward_parent` of Start, but for wrapping
 		wrapper: Option<NonZeroUsize>,
 		error: Option<Box<SyntaxError>>,
 	},
@@ -57,13 +57,14 @@
 		if self.offset == 0 {
 			return 0.into();
 		};
-		if let Some(lex) = self.lexemes.get(self.offset) {
-			lex.range.start()
-		} else if let Some(lex) = self.lexemes.get(self.offset - 1) {
-			lex.range.end()
-		} else {
-			panic!("hard oob")
-		}
+		self.lexemes.get(self.offset).map_or_else(
+			|| {
+				self.lexemes
+					.get(self.offset - 1)
+					.map_or_else(|| panic!("hard oob"), |lex| lex.range.end())
+			},
+			|lex| lex.range.start(),
+		)
 	}
 
 	pub(super) fn finish(mut self) -> Parse {
@@ -139,7 +140,7 @@
 						self.errors.push(LocatedSyntaxError {
 							error: *error,
 							range: TextRange::new(range.0, range.1),
-						})
+						});
 					}
 					self.builder.finish_node();
 					depth -= 1;
@@ -158,7 +159,7 @@
 								self.errors.push(LocatedSyntaxError {
 									error: *error,
 									range: TextRange::new(range.0, range.1),
-								})
+								});
 							}
 
 							if depth == 1 {
modifiedcrates/jrsonnet-stdlib/src/manifest/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/manifest/mod.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/mod.rs
@@ -98,6 +98,7 @@
 }
 
 #[builtin]
+#[allow(clippy::fn_params_excessive_bools)]
 pub fn builtin_manifest_yaml_stream(
 	value: Val,
 	#[default(false)] indent_array_in_object: bool,
modifiedcrates/jrsonnet-stdlib/src/manifest/yaml.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/manifest/yaml.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/yaml.rs
@@ -76,7 +76,7 @@
 
 	string.is_empty()
 		|| need_quotes_spaces(string)
-		|| string.starts_with(|c| matches!(c, '&' | '*' | '?' | '|' | '-' | '<' | '>' | '=' | '!' | '%' | '@'))
+		|| string.starts_with(['&' , '*' , '?' , '|' , '-' , '<' , '>' , '=' , '!' , '%' , '@'])
 		|| string.contains(|c| matches!(c, ':' | '{' | '}' | '[' | ']' | ',' | '#' | '`' | '\"' | '\'' | '\\' | '\0'..='\x06' | '\t' | '\n' | '\r' | '\x0e'..='\x1a' | '\x1c'..='\x1f'))
 		|| [
 			// http://yaml.org/type/bool.html
modifiedcrates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/strings.rs
+++ b/crates/jrsonnet-stdlib/src/strings.rs
@@ -267,6 +267,7 @@
 }
 
 #[cfg(test)]
+#[allow(clippy::float_cmp)]
 mod tests {
 	use super::*;
 
@@ -274,8 +275,8 @@
 	fn parse_nat_base_8() {
 		assert_eq!(parse_nat::<8>("0").unwrap(), 0.);
 		assert_eq!(parse_nat::<8>("5").unwrap(), 5.);
-		assert_eq!(parse_nat::<8>("32").unwrap(), 0o32 as f64);
-		assert_eq!(parse_nat::<8>("761").unwrap(), 0o761 as f64);
+		assert_eq!(parse_nat::<8>("32").unwrap(), f64::from(0o32));
+		assert_eq!(parse_nat::<8>("761").unwrap(), f64::from(0o761));
 	}
 
 	#[test]
@@ -290,7 +291,7 @@
 	fn parse_nat_base_16() {
 		assert_eq!(parse_nat::<16>("0").unwrap(), 0.);
 		assert_eq!(parse_nat::<16>("A").unwrap(), 10.);
-		assert_eq!(parse_nat::<16>("a9").unwrap(), 0xA9 as f64);
-		assert_eq!(parse_nat::<16>("BbC").unwrap(), 0xBBC as f64);
+		assert_eq!(parse_nat::<16>("a9").unwrap(), f64::from(0xA9));
+		assert_eq!(parse_nat::<16>("BbC").unwrap(), f64::from(0xBBC));
 	}
 }
modifiedcrates/jrsonnet-types/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-types/src/lib.rs
+++ b/crates/jrsonnet-types/src/lib.rs
@@ -4,84 +4,6 @@
 
 use jrsonnet_gcmodule::Trace;
 
-#[macro_export]
-macro_rules! ty {
-	((Array<number>)) => {{
-		$crate::ComplexValType::ArrayRef(&$crate::ComplexValType::Simple($crate::ValType::Num))
-	}};
-	((Array<ubyte>)) => {{
-		$crate::ComplexValType::ArrayRef(&$crate::ComplexValType::BoundedNumber(Some(0.0), Some(255.0)))
-	}};
-	(array) => {
-		$crate::ComplexValType::Simple($crate::ValType::Arr)
-	};
-	(boolean) => {
-		$crate::ComplexValType::Simple($crate::ValType::Bool)
-	};
-	(null) => {
-		$crate::ComplexValType::Simple($crate::ValType::Null)
-	};
-	(string) => {
-		$crate::ComplexValType::Simple($crate::ValType::Str)
-	};
-	(char) => {
-		$crate::ComplexValType::Char
-	};
-	(number) => {
-		$crate::ComplexValType::Simple($crate::ValType::Num)
-	};
-	(BoundedNumber<($min:expr), ($max:expr)>) => {{
-		$crate::ComplexValType::BoundedNumber($min, $max)
-	}};
-	(object) => {
-		$crate::ComplexValType::Simple($crate::ValType::Obj)
-	};
-	(any) => {
-		$crate::ComplexValType::Any
-	};
-	(function) => {
-		$crate::ComplexValType::Simple($crate::ValType::Func)
-	};
-	(($($a:tt) |+)) => {{
-		static CONTENTS: &'static [&'static $crate::ComplexValType] = &[
-			$(&ty!($a)),+
-		];
-		$crate::ComplexValType::UnionRef(CONTENTS)
-	}};
-	(($($a:tt) &+)) => {{
-		static CONTENTS: &'static [&'static $crate::ComplexValType] = &[
-			$(&ty!($a)),+
-		];
-		$crate::ComplexValType::SumRef(CONTENTS)
-	}};
-}
-
-#[test]
-fn test() {
-	assert_eq!(
-		ty!((Array<number>)),
-		ComplexValType::ArrayRef(&ComplexValType::Simple(ValType::Num))
-	);
-	assert_eq!(ty!(array), ComplexValType::Simple(ValType::Arr));
-	assert_eq!(ty!(any), ComplexValType::Any);
-	assert_eq!(
-		ty!((string | number)),
-		ComplexValType::UnionRef(&[
-			&ComplexValType::Simple(ValType::Str),
-			&ComplexValType::Simple(ValType::Num)
-		])
-	);
-	assert_eq!(
-		format!("{}", ty!(((string & number) | (object & null)))),
-		"string & number | object & null"
-	);
-	assert_eq!(format!("{}", ty!((string | array))), "string | array");
-	assert_eq!(
-		format!("{}", ty!(((string & number) | array))),
-		"string & number | array"
-	);
-}
-
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]
 pub enum ValType {
 	Bool,
@@ -213,98 +135,5 @@
 			Self::Lazy(lazy) => write!(f, "Lazy<{lazy}>")?,
 		};
 		Ok(())
-	}
-}
-
-peg::parser! {
-pub grammar parser() for str {
-	rule number() -> f64
-		= n:$(['0'..='9']+) { n.parse().unwrap() }
-
-	rule any_ty() -> ComplexValType = "any" { ComplexValType::Any }
-	rule char_ty() -> ComplexValType = "character" { ComplexValType::Char }
-	rule bool_ty() -> ComplexValType = "boolean" { ComplexValType::Simple(ValType::Bool) }
-	rule null_ty() -> ComplexValType = "null" { ComplexValType::Simple(ValType::Null) }
-	rule str_ty() -> ComplexValType = "string" { ComplexValType::Simple(ValType::Str) }
-	rule num_ty() -> ComplexValType = "number" { ComplexValType::Simple(ValType::Num) }
-	rule simple_array_ty() -> ComplexValType = "array" { ComplexValType::Simple(ValType::Arr) }
-	rule simple_object_ty() -> ComplexValType = "object" { ComplexValType::Simple(ValType::Obj) }
-	rule simple_function_ty() -> ComplexValType = "function" { ComplexValType::Simple(ValType::Func) }
-
-	rule array_ty() -> ComplexValType
-		= "Array<" t:ty() ">" { ComplexValType::Array(Box::new(t)) }
-
-	rule bounded_number_ty() -> ComplexValType
-		= "BoundedNumber<" a:number() ", " b:number() ">" { ComplexValType::BoundedNumber(Some(a), Some(b)) }
-
-	rule ty_basic() -> ComplexValType
-		= any_ty()
-		/ char_ty()
-		/ bool_ty()
-		/ null_ty()
-		/ str_ty()
-		/ num_ty()
-		/ simple_array_ty()
-		/ simple_object_ty()
-		/ simple_function_ty()
-		/ array_ty()
-		/ bounded_number_ty()
-
-	pub rule ty() -> ComplexValType
-		= precedence! {
-			a:(@) " | " b:@ {
-				match a {
-					ComplexValType::Union(mut a) => {
-						a.push(b);
-						ComplexValType::Union(a)
-					}
-					_ => ComplexValType::Union(vec![a, b]),
-				}
-			}
-			--
-			a:(@) " & " b:@ {
-				match a {
-					ComplexValType::Sum(mut a) => {
-						a.push(b);
-						ComplexValType::Sum(a)
-					}
-					_ => ComplexValType::Sum(vec![a, b]),
-				}
-			}
-			--
-			"(" t:ty() ")" { t }
-			t:ty_basic() { t }
-		}
-}
-}
-
-#[cfg(test)]
-pub mod tests {
-	use super::parser;
-
-	#[test]
-	fn precedence() {
-		assert_eq!(
-			parser::ty("(any & any) | (any | any) & any")
-				.unwrap()
-				.to_string(),
-			"any & any | (any | any) & any"
-		);
-	}
-
-	#[test]
-	fn array() {
-		assert_eq!(parser::ty("Array<any>").unwrap().to_string(), "array");
-		assert_eq!(
-			parser::ty("Array<number>").unwrap().to_string(),
-			"Array<number>"
-		);
-	}
-	#[test]
-	fn bounded_number() {
-		assert_eq!(
-			parser::ty("BoundedNumber<1, 2>").unwrap().to_string(),
-			"BoundedNumber<1, 2>"
-		);
 	}
 }
modifiedflake.lockdiffbeforeafterboth
--- a/flake.lock
+++ b/flake.lock
@@ -7,11 +7,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1715274763,
-        "narHash": "sha256-3Iv1PGHJn9sV3HO4FlOVaaztOxa9uGLfOmUWrH7v7+A=",
+        "lastModified": 1724377159,
+        "narHash": "sha256-ixjje1JO8ucKT41hs6n2NCde1Vc0+Zc2p2gUbJpCsMw=",
         "owner": "ipetkov",
         "repo": "crane",
-        "rev": "27025ab71bdca30e7ed0a16c88fd74c5970fc7f5",
+        "rev": "3e47b7a86c19142bd3675da49d6acef488b4dac1",
         "type": "github"
       },
       "original": {
@@ -27,44 +27,26 @@
         ]
       },
       "locked": {
-        "lastModified": 1717285511,
-        "narHash": "sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw=",
+        "lastModified": 1722555600,
+        "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=",
         "owner": "hercules-ci",
         "repo": "flake-parts",
-        "rev": "2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8",
+        "rev": "8471fe90ad337a8074e957b69ca4d0089218391d",
         "type": "github"
       },
       "original": {
         "owner": "hercules-ci",
         "repo": "flake-parts",
-        "type": "github"
-      }
-    },
-    "flake-utils": {
-      "inputs": {
-        "systems": "systems"
-      },
-      "locked": {
-        "lastModified": 1710146030,
-        "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
         "type": "github"
       }
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1715551360,
-        "narHash": "sha256-fGYt2XnTYUS4Q0eH8tVu3ki1+m9YTgZ+NjlfkMKzko0=",
+        "lastModified": 1724519568,
+        "narHash": "sha256-CmfrenY4cEi/mIslKy8XOGdqxUUVgT6/qMzNcAN/7z8=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "836306cd7bbb9e0f89c557b2ae14df09e573ee89",
+        "rev": "eb0e6df0cdd2641ec0651cd9802ff4cff3b3e915",
         "type": "github"
       },
       "original": {
@@ -84,17 +66,16 @@
     },
     "rust-overlay": {
       "inputs": {
-        "flake-utils": "flake-utils",
         "nixpkgs": [
           "nixpkgs"
         ]
       },
       "locked": {
-        "lastModified": 1715480255,
-        "narHash": "sha256-gEZl8nYidQwqJhOigJ91JDjoBFoPEWVsd82AKnaE7Go=",
+        "lastModified": 1724466314,
+        "narHash": "sha256-ltKuK6shQ64uej1mYNtBsDYxttUNFiv9AcHqk0+0NQM=",
         "owner": "oxalica",
         "repo": "rust-overlay",
-        "rev": "d690205a4f01ec0930303c4204e5063958e51255",
+        "rev": "2b5b3edd96ef336b00622dcabc13788fdef9e3ca",
         "type": "github"
       },
       "original": {
@@ -123,21 +104,6 @@
       "original": {
         "owner": "CertainLach",
         "repo": "shelly",
-        "type": "github"
-      }
-    },
-    "systems": {
-      "locked": {
-        "lastModified": 1681028828,
-        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
-        "owner": "nix-systems",
-        "repo": "default",
-        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-systems",
-        "repo": "default",
         "type": "github"
       }
     }
modifiedrust-toolchain.tomldiffbeforeafterboth
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2024-05-10"
+channel = "nightly-2024-08-20"
 components = ["rustfmt", "clippy", "rust-analyzer", "rust-src"]
modifiedtests/tests/builtin.rsdiffbeforeafterboth
--- a/tests/tests/builtin.rs
+++ b/tests/tests/builtin.rs
@@ -11,8 +11,8 @@
 use jrsonnet_stdlib::ContextInitializer as StdContextInitializer;
 
 #[builtin]
-fn a() -> Result<u32> {
-	Ok(1)
+fn a() -> u32 {
+	1
 }
 
 #[test]
@@ -29,8 +29,8 @@
 }
 
 #[builtin]
-fn native_add(a: u32, b: u32) -> Result<u32> {
-	Ok(a + b)
+fn native_add(a: u32, b: u32) -> u32 {
+	a + b
 }
 #[derive(Trace)]
 struct NativeAddContextInitializer;
@@ -72,13 +72,13 @@
 #[builtin(fields(
     a: u32
 ))]
-fn curried_add(this: &curried_add, b: u32) -> Result<u32> {
-	Ok(this.a + b)
+fn curried_add(this: &curried_add, b: u32) -> u32 {
+	this.a + b
 }
 
 #[builtin]
-fn curry_add(a: u32) -> Result<FuncVal> {
-	Ok(FuncVal::builtin(curried_add { a }))
+fn curry_add(a: u32) -> FuncVal {
+	FuncVal::builtin(curried_add { a })
 }
 #[derive(Trace)]
 struct CurryAddContextInitializer;
modifiedtests/tests/common.rsdiffbeforeafterboth
--- a/tests/tests/common.rs
+++ b/tests/tests/common.rs
@@ -73,6 +73,7 @@
 			.iter()
 			.map(|p| p.name().as_str().unwrap_or("<unnamed>").to_string())
 			.collect(),
+		FuncVal::Thunk(_) => vec![],
 	}
 }
 
modifiedtests/tests/golden.rsdiffbeforeafterboth
--- a/tests/tests/golden.rs
+++ b/tests/tests/golden.rs
@@ -32,7 +32,7 @@
 		Err(e) => return trace_format.format(&e).unwrap(),
 	};
 	match v.manifest(JsonFormat::default()) {
-		Ok(v) => v.to_string(),
+		Ok(v) => v,
 		Err(e) => trace_format.format(&e).unwrap(),
 	}
 }
@@ -57,45 +57,46 @@
 
 		if !golden_path.exists() {
 			fs::write(golden_path, &result)?;
-		} else {
-			let golden = fs::read_to_string(golden_path)?;
+			continue;
+		}
 
-			match (serde_json::from_str(&result), serde_json::from_str(&golden)) {
-				(Err(_), Ok(_)) => assert_eq!(
-					result,
-					golden,
-					"unexpected error for golden {}",
-					entry.path().display()
-				),
-				(Ok(_), Err(_)) => assert_eq!(
-					result,
-					golden,
-					"expected error for golden {}",
-					entry.path().display()
-				),
-				(Ok(result), Ok(golden)) => {
-					// Show diff relative to golden`.
-					let diff = JsonDiff::diff_string(&golden, &result, false);
-					if let Some(diff) = diff {
-						panic!(
-							"Result \n{result:#}\n\
-								and golden \n{golden:#}\n\
-								did not match structurally:\n{diff:#}\n\
-								for golden {}",
-							entry.path().display()
-						);
-					}
-				}
-				(Err(_), Err(_)) => {}
-			};
+		let golden = fs::read_to_string(golden_path)?;
 
-			assert_eq!(
+		match (serde_json::from_str(&result), serde_json::from_str(&golden)) {
+			(Err(_), Ok(_)) => assert_eq!(
 				result,
 				golden,
-				"golden didn't match for {}",
+				"unexpected error for golden {}",
+				entry.path().display()
+			),
+			(Ok(_), Err(_)) => assert_eq!(
+				result,
+				golden,
+				"expected error for golden {}",
 				entry.path().display()
-			)
-		}
+			),
+			(Ok(result), Ok(golden)) => {
+				// Show diff relative to golden`.
+				let diff = JsonDiff::diff_string(&golden, &result, false);
+				if let Some(diff) = diff {
+					panic!(
+						"Result \n{result:#}\n\
+							and golden \n{golden:#}\n\
+							did not match structurally:\n{diff:#}\n\
+							for golden {}",
+						entry.path().display()
+					);
+				}
+			}
+			(Err(_), Err(_)) => {}
+		};
+
+		assert_eq!(
+			result,
+			golden,
+			"golden didn't match for {}",
+			entry.path().display()
+		);
 	}
 
 	Ok(())