ALPHA: wire is a tool to deploy nixos systems wire.althaea.zone/

caching & bench runner improvements (#356)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

authored by

marshmallow
autofix-ci[bot]
and committed by
GitHub
e8623729 b4bd1ef1

+1576 -155
+3
.cargo/config.toml
··· 1 1 [profile.profiling] 2 2 inherits = "release" 3 3 debug = true 4 + 5 + [build] 6 + rustflags = ["--cfg=sqlx_macros_unstable"]
+1
.env
··· 1 + DATABASE_URL=sqlite://dev.db
+4 -2
.github/workflows/autofix.yml
··· 36 36 ~/.cargo/registry/cache/ 37 37 ~/.cargo/git/db/ 38 38 key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 39 + - name: setup sqlx 40 + run: nix develop -L -v -c sqlx database setup --source ./wire/lib/src/cache/migrations/ 39 41 - name: clippy --fix 40 - run: nix develop --print-build-logs -v --command cargo clippy --fix 42 + run: nix develop -L -v -c cargo clippy --fix 41 43 - name: pre-commit run 42 - run: nix develop --print-build-logs -v --command pre-commit run --all-files 44 + run: nix develop -L -v -c pre-commit run --all-files 43 45 continue-on-error: true 44 46 - name: Upgrade Hash 45 47 if: ${{ needs.check-changes.outputs.docs-pnpm == 'true' }}
+4
.gitignore
··· 5 5 .pre-commit-config.yaml 6 6 *.qcow2 7 7 mutants*/ 8 + *.db 9 + run.json 10 + stats.md 11 + .nixos-test-history
+12
.sqlx/query-1ab95659223cbc7e012f538080c4be8b4774a0354348e6de53e6ea7aadfe8819.json
··· 1 + { 2 + "db_name": "SQLite", 3 + "query": "delete from inspection_cache\nwhere\n blob_id in (\n select\n id\n from\n inspection_blobs\n where\n schema_version != $1\n )\n or ROWID in (\n select\n ROWID\n from\n inspection_cache\n order by\n ROWID desc\n limit\n -1\n offset\n 30\n )", 4 + "describe": { 5 + "columns": [], 6 + "parameters": { 7 + "Right": 1 8 + }, 9 + "nullable": [] 10 + }, 11 + "hash": "1ab95659223cbc7e012f538080c4be8b4774a0354348e6de53e6ea7aadfe8819" 12 + }
+20
.sqlx/query-5513de9a13d5ef4f8d667159c4f7a1e487ae03e2ec7b03b754e5d08076b0d78d.json
··· 1 + { 2 + "db_name": "SQLite", 3 + "query": "\n select inspection_blobs.json_value from inspection_blobs\n join inspection_cache\n on inspection_cache.blob_id = inspection_blobs.id\n where inspection_cache.store_path = $1\n and inspection_cache.hash = $2\n and inspection_blobs.schema_version = $3\n limit 1\n ", 4 + "describe": { 5 + "columns": [ 6 + { 7 + "name": "json_value", 8 + "ordinal": 0, 9 + "type_info": "Blob" 10 + } 11 + ], 12 + "parameters": { 13 + "Right": 3 14 + }, 15 + "nullable": [ 16 + false 17 + ] 18 + }, 19 + "hash": "5513de9a13d5ef4f8d667159c4f7a1e487ae03e2ec7b03b754e5d08076b0d78d" 20 + }
+12
.sqlx/query-61b79ce83349770c0b4e474471cdee067214b88444cb68d6d3560f4be835b3a8.json
··· 1 + { 2 + "db_name": "SQLite", 3 + "query": "\n insert into\n inspection_cache (store_path, hash, blob_id)\n values\n ($1, $2, $3)\n ", 4 + "describe": { 5 + "columns": [], 6 + "parameters": { 7 + "Right": 3 8 + }, 9 + "nullable": [] 10 + }, 11 + "hash": "61b79ce83349770c0b4e474471cdee067214b88444cb68d6d3560f4be835b3a8" 12 + }
+12
.sqlx/query-a5459b4c5f879509d29c3b6dcf85d1da89e4bd9380b8763edc5d16cbaa302d3f.json
··· 1 + { 2 + "db_name": "SQLite", 3 + "query": "delete from inspection_blobs\nwhere\n not exists (\n select\n 1\n from\n inspection_cache\n where\n inspection_cache.blob_id = inspection_blobs.id\n )", 4 + "describe": { 5 + "columns": [], 6 + "parameters": { 7 + "Right": 0 8 + }, 9 + "nullable": [] 10 + }, 11 + "hash": "a5459b4c5f879509d29c3b6dcf85d1da89e4bd9380b8763edc5d16cbaa302d3f" 12 + }
+20
.sqlx/query-cdea7b20c482f4127bacb5c58755d4fdd4dca2066b9c06950be60bc790569335.json
··· 1 + { 2 + "db_name": "SQLite", 3 + "query": "\n insert into inspection_blobs (json_value, schema_version)\n values ($1, $2)\n on conflict(json_value)\n do update set json_value = excluded.json_value\n returning inspection_blobs.id\n ", 4 + "describe": { 5 + "columns": [ 6 + { 7 + "name": "id", 8 + "ordinal": 0, 9 + "type_info": "Integer" 10 + } 11 + ], 12 + "parameters": { 13 + "Right": 2 14 + }, 15 + "nullable": [ 16 + false 17 + ] 18 + }, 19 + "hash": "cdea7b20c482f4127bacb5c58755d4fdd4dca2066b9c06950be60bc790569335" 20 + }
+1
CHANGELOG.md
··· 10 10 ### Added 11 11 12 12 - `meta.nodeNixpkgs` was implemented. 13 + - Caching of hive evaluation for flakes. 13 14 14 15 ## [v1.0.0-alpha.1] - 2025-11-24 15 16
+749 -1
Cargo.lock
··· 27 27 ] 28 28 29 29 [[package]] 30 + name = "allocator-api2" 31 + version = "0.2.21" 32 + source = "registry+https://github.com/rust-lang/crates.io-index" 33 + checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 34 + 35 + [[package]] 30 36 name = "anstream" 31 37 version = "0.6.20" 32 38 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 83 89 checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 84 90 85 91 [[package]] 92 + name = "atoi" 93 + version = "2.0.0" 94 + source = "registry+https://github.com/rust-lang/crates.io-index" 95 + checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" 96 + dependencies = [ 97 + "num-traits", 98 + ] 99 + 100 + [[package]] 86 101 name = "autocfg" 87 102 version = "1.5.0" 88 103 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 135 150 version = "2.9.1" 136 151 source = "registry+https://github.com/rust-lang/crates.io-index" 137 152 checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 153 + dependencies = [ 154 + "serde", 155 + ] 138 156 139 157 [[package]] 140 158 name = "bitmaps" ··· 164 182 "regex-automata", 165 183 "serde", 166 184 ] 185 + 186 + [[package]] 187 + name = "byteorder" 188 + version = "1.5.0" 189 + source = "registry+https://github.com/rust-lang/crates.io-index" 190 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 167 191 168 192 [[package]] 169 193 name = "bytes" ··· 178 202 checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" 179 203 dependencies = [ 180 204 "find-msvc-tools", 205 + "jobserver", 206 + "libc", 181 207 "shlex", 182 208 ] 183 209 ··· 256 282 checksum = "39615915e2ece2550c0149addac32fb5bd312c657f43845bb9088cb9c8a7c992" 257 283 dependencies = [ 258 284 "clap", 285 + "clap_lex", 286 + "is_executable", 287 + "shlex", 259 288 ] 260 289 261 290 [[package]] ··· 283 312 checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 284 313 285 314 [[package]] 315 + name = "concurrent-queue" 316 + version = "2.5.0" 317 + source = "registry+https://github.com/rust-lang/crates.io-index" 318 + checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 319 + dependencies = [ 320 + "crossbeam-utils", 321 + ] 322 + 323 + [[package]] 286 324 name = "const-oid" 287 325 version = "0.9.6" 288 326 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 298 336 ] 299 337 300 338 [[package]] 339 + name = "crc" 340 + version = "3.3.0" 341 + source = "registry+https://github.com/rust-lang/crates.io-index" 342 + checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" 343 + dependencies = [ 344 + "crc-catalog", 345 + ] 346 + 347 + [[package]] 348 + name = "crc-catalog" 349 + version = "2.4.0" 350 + source = "registry+https://github.com/rust-lang/crates.io-index" 351 + checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" 352 + 353 + [[package]] 354 + name = "crossbeam-queue" 355 + version = "0.3.12" 356 + source = "registry+https://github.com/rust-lang/crates.io-index" 357 + checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" 358 + dependencies = [ 359 + "crossbeam-utils", 360 + ] 361 + 362 + [[package]] 363 + name = "crossbeam-utils" 364 + version = "0.8.21" 365 + source = "registry+https://github.com/rust-lang/crates.io-index" 366 + checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 367 + 368 + [[package]] 301 369 name = "crypto-common" 302 370 version = "0.1.6" 303 371 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 382 450 checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" 383 451 dependencies = [ 384 452 "const-oid", 453 + "pem-rfc7468", 385 454 "zeroize", 386 455 ] 387 456 ··· 429 498 checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 430 499 dependencies = [ 431 500 "block-buffer", 501 + "const-oid", 432 502 "crypto-common", 503 + "subtle", 433 504 ] 434 505 435 506 [[package]] ··· 444 515 ] 445 516 446 517 [[package]] 518 + name = "dotenvy" 519 + version = "0.15.7" 520 + source = "registry+https://github.com/rust-lang/crates.io-index" 521 + checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" 522 + 523 + [[package]] 447 524 name = "downcast-rs" 448 525 version = "1.2.1" 449 526 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 478 555 version = "1.15.0" 479 556 source = "registry+https://github.com/rust-lang/crates.io-index" 480 557 checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 558 + dependencies = [ 559 + "serde", 560 + ] 481 561 482 562 [[package]] 483 563 name = "enum-display-derive" ··· 519 599 ] 520 600 521 601 [[package]] 602 + name = "etcetera" 603 + version = "0.8.0" 604 + source = "registry+https://github.com/rust-lang/crates.io-index" 605 + checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" 606 + dependencies = [ 607 + "cfg-if", 608 + "home", 609 + "windows-sys 0.48.0", 610 + ] 611 + 612 + [[package]] 613 + name = "event-listener" 614 + version = "5.4.1" 615 + source = "registry+https://github.com/rust-lang/crates.io-index" 616 + checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" 617 + dependencies = [ 618 + "concurrent-queue", 619 + "parking", 620 + "pin-project-lite", 621 + ] 622 + 623 + [[package]] 522 624 name = "fastrand" 523 625 version = "2.3.0" 524 626 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 554 656 checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" 555 657 556 658 [[package]] 659 + name = "flume" 660 + version = "0.11.1" 661 + source = "registry+https://github.com/rust-lang/crates.io-index" 662 + checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" 663 + dependencies = [ 664 + "futures-core", 665 + "futures-sink", 666 + "spin", 667 + ] 668 + 669 + [[package]] 557 670 name = "fnv" 558 671 version = "1.0.7" 559 672 source = "registry+https://github.com/rust-lang/crates.io-index" 560 673 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 561 674 562 675 [[package]] 676 + name = "foldhash" 677 + version = "0.1.5" 678 + source = "registry+https://github.com/rust-lang/crates.io-index" 679 + checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 680 + 681 + [[package]] 563 682 name = "form_urlencoded" 564 683 version = "1.2.2" 565 684 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 614 733 "futures-core", 615 734 "futures-task", 616 735 "futures-util", 736 + ] 737 + 738 + [[package]] 739 + name = "futures-intrusive" 740 + version = "0.5.0" 741 + source = "registry+https://github.com/rust-lang/crates.io-index" 742 + checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" 743 + dependencies = [ 744 + "futures-core", 745 + "lock_api", 746 + "parking_lot", 617 747 ] 618 748 619 749 [[package]] ··· 723 853 version = "0.15.5" 724 854 source = "registry+https://github.com/rust-lang/crates.io-index" 725 855 checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 856 + dependencies = [ 857 + "allocator-api2", 858 + "equivalent", 859 + "foldhash", 860 + ] 861 + 862 + [[package]] 863 + name = "hashlink" 864 + version = "0.10.0" 865 + source = "registry+https://github.com/rust-lang/crates.io-index" 866 + checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" 867 + dependencies = [ 868 + "hashbrown", 869 + ] 726 870 727 871 [[package]] 728 872 name = "heck" ··· 735 879 version = "0.5.2" 736 880 source = "registry+https://github.com/rust-lang/crates.io-index" 737 881 checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" 882 + 883 + [[package]] 884 + name = "hex" 885 + version = "0.4.3" 886 + source = "registry+https://github.com/rust-lang/crates.io-index" 887 + checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 888 + 889 + [[package]] 890 + name = "hkdf" 891 + version = "0.12.4" 892 + source = "registry+https://github.com/rust-lang/crates.io-index" 893 + checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" 894 + dependencies = [ 895 + "hmac", 896 + ] 897 + 898 + [[package]] 899 + name = "hmac" 900 + version = "0.12.1" 901 + source = "registry+https://github.com/rust-lang/crates.io-index" 902 + checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 903 + dependencies = [ 904 + "digest", 905 + ] 906 + 907 + [[package]] 908 + name = "home" 909 + version = "0.5.12" 910 + source = "registry+https://github.com/rust-lang/crates.io-index" 911 + checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" 912 + dependencies = [ 913 + "windows-sys 0.61.2", 914 + ] 738 915 739 916 [[package]] 740 917 name = "icu_collections" ··· 892 1069 checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" 893 1070 894 1071 [[package]] 1072 + name = "is_executable" 1073 + version = "1.0.5" 1074 + source = "registry+https://github.com/rust-lang/crates.io-index" 1075 + checksum = "baabb8b4867b26294d818bf3f651a454b6901431711abb96e296245888d6e8c4" 1076 + dependencies = [ 1077 + "windows-sys 0.60.2", 1078 + ] 1079 + 1080 + [[package]] 895 1081 name = "is_terminal_polyfill" 896 1082 version = "1.70.1" 897 1083 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 913 1099 checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 914 1100 915 1101 [[package]] 1102 + name = "jobserver" 1103 + version = "0.1.34" 1104 + source = "registry+https://github.com/rust-lang/crates.io-index" 1105 + checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" 1106 + dependencies = [ 1107 + "getrandom 0.3.3", 1108 + "libc", 1109 + ] 1110 + 1111 + [[package]] 916 1112 name = "key_agent" 917 1113 version = "1.0.0-alpha.1" 918 1114 dependencies = [ ··· 932 1128 version = "1.5.0" 933 1129 source = "registry+https://github.com/rust-lang/crates.io-index" 934 1130 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1131 + dependencies = [ 1132 + "spin", 1133 + ] 935 1134 936 1135 [[package]] 937 1136 name = "lib" ··· 960 1159 "serde", 961 1160 "serde_json", 962 1161 "sha2", 1162 + "sqlx", 963 1163 "strip-ansi-escapes", 964 1164 "syn 2.0.111", 965 1165 "tempdir", ··· 968 1168 "tokio", 969 1169 "tokio-util", 970 1170 "tracing", 1171 + "zstd", 971 1172 ] 972 1173 973 1174 [[package]] ··· 977 1178 checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" 978 1179 979 1180 [[package]] 1181 + name = "libm" 1182 + version = "0.2.15" 1183 + source = "registry+https://github.com/rust-lang/crates.io-index" 1184 + checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" 1185 + 1186 + [[package]] 980 1187 name = "libmimalloc-sys" 981 1188 version = "0.1.44" 982 1189 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 987 1194 ] 988 1195 989 1196 [[package]] 1197 + name = "libredox" 1198 + version = "0.1.10" 1199 + source = "registry+https://github.com/rust-lang/crates.io-index" 1200 + checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" 1201 + dependencies = [ 1202 + "bitflags 2.9.1", 1203 + "libc", 1204 + "redox_syscall", 1205 + ] 1206 + 1207 + [[package]] 1208 + name = "libsqlite3-sys" 1209 + version = "0.30.1" 1210 + source = "registry+https://github.com/rust-lang/crates.io-index" 1211 + checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" 1212 + dependencies = [ 1213 + "cc", 1214 + "pkg-config", 1215 + "vcpkg", 1216 + ] 1217 + 1218 + [[package]] 990 1219 name = "linux-raw-sys" 991 1220 version = "0.9.4" 992 1221 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1015 1244 checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 1016 1245 1017 1246 [[package]] 1247 + name = "md-5" 1248 + version = "0.10.6" 1249 + source = "registry+https://github.com/rust-lang/crates.io-index" 1250 + checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" 1251 + dependencies = [ 1252 + "cfg-if", 1253 + "digest", 1254 + ] 1255 + 1256 + [[package]] 1018 1257 name = "memchr" 1019 1258 version = "2.7.5" 1020 1259 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1171 1410 ] 1172 1411 1173 1412 [[package]] 1413 + name = "num-bigint-dig" 1414 + version = "0.8.6" 1415 + source = "registry+https://github.com/rust-lang/crates.io-index" 1416 + checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" 1417 + dependencies = [ 1418 + "lazy_static", 1419 + "libm", 1420 + "num-integer", 1421 + "num-iter", 1422 + "num-traits", 1423 + "rand 0.8.5", 1424 + "smallvec", 1425 + "zeroize", 1426 + ] 1427 + 1428 + [[package]] 1429 + name = "num-integer" 1430 + version = "0.1.46" 1431 + source = "registry+https://github.com/rust-lang/crates.io-index" 1432 + checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 1433 + dependencies = [ 1434 + "num-traits", 1435 + ] 1436 + 1437 + [[package]] 1438 + name = "num-iter" 1439 + version = "0.1.45" 1440 + source = "registry+https://github.com/rust-lang/crates.io-index" 1441 + checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" 1442 + dependencies = [ 1443 + "autocfg", 1444 + "num-integer", 1445 + "num-traits", 1446 + ] 1447 + 1448 + [[package]] 1174 1449 name = "num-traits" 1175 1450 version = "0.2.19" 1176 1451 source = "registry+https://github.com/rust-lang/crates.io-index" 1177 1452 checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1178 1453 dependencies = [ 1179 1454 "autocfg", 1455 + "libm", 1180 1456 ] 1181 1457 1182 1458 [[package]] ··· 1237 1513 "supports-color 2.1.0", 1238 1514 "supports-color 3.0.2", 1239 1515 ] 1516 + 1517 + [[package]] 1518 + name = "parking" 1519 + version = "2.2.1" 1520 + source = "registry+https://github.com/rust-lang/crates.io-index" 1521 + checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" 1240 1522 1241 1523 [[package]] 1242 1524 name = "parking_lot" ··· 1262 1544 ] 1263 1545 1264 1546 [[package]] 1547 + name = "pem-rfc7468" 1548 + version = "0.7.0" 1549 + source = "registry+https://github.com/rust-lang/crates.io-index" 1550 + checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" 1551 + dependencies = [ 1552 + "base64ct", 1553 + ] 1554 + 1555 + [[package]] 1265 1556 name = "percent-encoding" 1266 1557 version = "2.3.2" 1267 1558 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1290 1581 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1291 1582 1292 1583 [[package]] 1584 + name = "pkcs1" 1585 + version = "0.7.5" 1586 + source = "registry+https://github.com/rust-lang/crates.io-index" 1587 + checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" 1588 + dependencies = [ 1589 + "der", 1590 + "pkcs8", 1591 + "spki", 1592 + ] 1593 + 1594 + [[package]] 1293 1595 name = "pkcs8" 1294 1596 version = "0.10.2" 1295 1597 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1298 1600 "der", 1299 1601 "spki", 1300 1602 ] 1603 + 1604 + [[package]] 1605 + name = "pkg-config" 1606 + version = "0.3.32" 1607 + source = "registry+https://github.com/rust-lang/crates.io-index" 1608 + checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 1301 1609 1302 1610 [[package]] 1303 1611 name = "portable-pty" ··· 1448 1756 1449 1757 [[package]] 1450 1758 name = "rand" 1759 + version = "0.8.5" 1760 + source = "registry+https://github.com/rust-lang/crates.io-index" 1761 + checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1762 + dependencies = [ 1763 + "libc", 1764 + "rand_chacha 0.3.1", 1765 + "rand_core 0.6.4", 1766 + ] 1767 + 1768 + [[package]] 1769 + name = "rand" 1451 1770 version = "0.9.2" 1452 1771 source = "registry+https://github.com/rust-lang/crates.io-index" 1453 1772 checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 1454 1773 dependencies = [ 1455 - "rand_chacha", 1774 + "rand_chacha 0.9.0", 1456 1775 "rand_core 0.9.3", 1457 1776 ] 1458 1777 1459 1778 [[package]] 1460 1779 name = "rand_chacha" 1780 + version = "0.3.1" 1781 + source = "registry+https://github.com/rust-lang/crates.io-index" 1782 + checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1783 + dependencies = [ 1784 + "ppv-lite86", 1785 + "rand_core 0.6.4", 1786 + ] 1787 + 1788 + [[package]] 1789 + name = "rand_chacha" 1461 1790 version = "0.9.0" 1462 1791 source = "registry+https://github.com/rust-lang/crates.io-index" 1463 1792 checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" ··· 1565 1894 ] 1566 1895 1567 1896 [[package]] 1897 + name = "rsa" 1898 + version = "0.9.9" 1899 + source = "registry+https://github.com/rust-lang/crates.io-index" 1900 + checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" 1901 + dependencies = [ 1902 + "const-oid", 1903 + "digest", 1904 + "num-bigint-dig", 1905 + "num-integer", 1906 + "num-traits", 1907 + "pkcs1", 1908 + "pkcs8", 1909 + "rand_core 0.6.4", 1910 + "signature", 1911 + "spki", 1912 + "subtle", 1913 + "zeroize", 1914 + ] 1915 + 1916 + [[package]] 1568 1917 name = "rustc-demangle" 1569 1918 version = "0.1.26" 1570 1919 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1666 2015 ] 1667 2016 1668 2017 [[package]] 2018 + name = "serde_urlencoded" 2019 + version = "0.7.1" 2020 + source = "registry+https://github.com/rust-lang/crates.io-index" 2021 + checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 2022 + dependencies = [ 2023 + "form_urlencoded", 2024 + "itoa", 2025 + "ryu", 2026 + "serde", 2027 + ] 2028 + 2029 + [[package]] 1669 2030 name = "serde_with" 1670 2031 version = "3.15.0" 1671 2032 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1696 2057 "cfg-if", 1697 2058 "libc", 1698 2059 "winapi", 2060 + ] 2061 + 2062 + [[package]] 2063 + name = "sha1" 2064 + version = "0.10.6" 2065 + source = "registry+https://github.com/rust-lang/crates.io-index" 2066 + checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 2067 + dependencies = [ 2068 + "cfg-if", 2069 + "cpufeatures", 2070 + "digest", 1699 2071 ] 1700 2072 1701 2073 [[package]] ··· 1755 2127 source = "registry+https://github.com/rust-lang/crates.io-index" 1756 2128 checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" 1757 2129 dependencies = [ 2130 + "digest", 1758 2131 "rand_core 0.6.4", 1759 2132 ] 1760 2133 ··· 1779 2152 version = "1.15.1" 1780 2153 source = "registry+https://github.com/rust-lang/crates.io-index" 1781 2154 checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 2155 + dependencies = [ 2156 + "serde", 2157 + ] 1782 2158 1783 2159 [[package]] 1784 2160 name = "socket2" ··· 1788 2164 dependencies = [ 1789 2165 "libc", 1790 2166 "windows-sys 0.59.0", 2167 + ] 2168 + 2169 + [[package]] 2170 + name = "spin" 2171 + version = "0.9.8" 2172 + source = "registry+https://github.com/rust-lang/crates.io-index" 2173 + checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 2174 + dependencies = [ 2175 + "lock_api", 1791 2176 ] 1792 2177 1793 2178 [[package]] ··· 1801 2186 ] 1802 2187 1803 2188 [[package]] 2189 + name = "sqlx" 2190 + version = "0.8.6" 2191 + source = "registry+https://github.com/rust-lang/crates.io-index" 2192 + checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" 2193 + dependencies = [ 2194 + "sqlx-core", 2195 + "sqlx-macros", 2196 + "sqlx-mysql", 2197 + "sqlx-postgres", 2198 + "sqlx-sqlite", 2199 + ] 2200 + 2201 + [[package]] 2202 + name = "sqlx-core" 2203 + version = "0.8.6" 2204 + source = "registry+https://github.com/rust-lang/crates.io-index" 2205 + checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" 2206 + dependencies = [ 2207 + "base64", 2208 + "bytes", 2209 + "crc", 2210 + "crossbeam-queue", 2211 + "either", 2212 + "event-listener", 2213 + "futures-core", 2214 + "futures-intrusive", 2215 + "futures-io", 2216 + "futures-util", 2217 + "hashbrown", 2218 + "hashlink", 2219 + "indexmap", 2220 + "log", 2221 + "memchr", 2222 + "once_cell", 2223 + "percent-encoding", 2224 + "serde", 2225 + "serde_json", 2226 + "sha2", 2227 + "smallvec", 2228 + "thiserror 2.0.17", 2229 + "tokio", 2230 + "tokio-stream", 2231 + "tracing", 2232 + "url", 2233 + ] 2234 + 2235 + [[package]] 2236 + name = "sqlx-macros" 2237 + version = "0.8.6" 2238 + source = "registry+https://github.com/rust-lang/crates.io-index" 2239 + checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" 2240 + dependencies = [ 2241 + "proc-macro2", 2242 + "quote", 2243 + "sqlx-core", 2244 + "sqlx-macros-core", 2245 + "syn 2.0.111", 2246 + ] 2247 + 2248 + [[package]] 2249 + name = "sqlx-macros-core" 2250 + version = "0.8.6" 2251 + source = "registry+https://github.com/rust-lang/crates.io-index" 2252 + checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" 2253 + dependencies = [ 2254 + "dotenvy", 2255 + "either", 2256 + "heck", 2257 + "hex", 2258 + "once_cell", 2259 + "proc-macro2", 2260 + "quote", 2261 + "serde", 2262 + "serde_json", 2263 + "sha2", 2264 + "sqlx-core", 2265 + "sqlx-mysql", 2266 + "sqlx-postgres", 2267 + "sqlx-sqlite", 2268 + "syn 2.0.111", 2269 + "tokio", 2270 + "url", 2271 + ] 2272 + 2273 + [[package]] 2274 + name = "sqlx-mysql" 2275 + version = "0.8.6" 2276 + source = "registry+https://github.com/rust-lang/crates.io-index" 2277 + checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" 2278 + dependencies = [ 2279 + "atoi", 2280 + "base64", 2281 + "bitflags 2.9.1", 2282 + "byteorder", 2283 + "bytes", 2284 + "crc", 2285 + "digest", 2286 + "dotenvy", 2287 + "either", 2288 + "futures-channel", 2289 + "futures-core", 2290 + "futures-io", 2291 + "futures-util", 2292 + "generic-array", 2293 + "hex", 2294 + "hkdf", 2295 + "hmac", 2296 + "itoa", 2297 + "log", 2298 + "md-5", 2299 + "memchr", 2300 + "once_cell", 2301 + "percent-encoding", 2302 + "rand 0.8.5", 2303 + "rsa", 2304 + "serde", 2305 + "sha1", 2306 + "sha2", 2307 + "smallvec", 2308 + "sqlx-core", 2309 + "stringprep", 2310 + "thiserror 2.0.17", 2311 + "tracing", 2312 + "whoami", 2313 + ] 2314 + 2315 + [[package]] 2316 + name = "sqlx-postgres" 2317 + version = "0.8.6" 2318 + source = "registry+https://github.com/rust-lang/crates.io-index" 2319 + checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" 2320 + dependencies = [ 2321 + "atoi", 2322 + "base64", 2323 + "bitflags 2.9.1", 2324 + "byteorder", 2325 + "crc", 2326 + "dotenvy", 2327 + "etcetera", 2328 + "futures-channel", 2329 + "futures-core", 2330 + "futures-util", 2331 + "hex", 2332 + "hkdf", 2333 + "hmac", 2334 + "home", 2335 + "itoa", 2336 + "log", 2337 + "md-5", 2338 + "memchr", 2339 + "once_cell", 2340 + "rand 0.8.5", 2341 + "serde", 2342 + "serde_json", 2343 + "sha2", 2344 + "smallvec", 2345 + "sqlx-core", 2346 + "stringprep", 2347 + "thiserror 2.0.17", 2348 + "tracing", 2349 + "whoami", 2350 + ] 2351 + 2352 + [[package]] 2353 + name = "sqlx-sqlite" 2354 + version = "0.8.6" 2355 + source = "registry+https://github.com/rust-lang/crates.io-index" 2356 + checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" 2357 + dependencies = [ 2358 + "atoi", 2359 + "flume", 2360 + "futures-channel", 2361 + "futures-core", 2362 + "futures-executor", 2363 + "futures-intrusive", 2364 + "futures-util", 2365 + "libsqlite3-sys", 2366 + "log", 2367 + "percent-encoding", 2368 + "serde", 2369 + "serde_urlencoded", 2370 + "sqlx-core", 2371 + "thiserror 2.0.17", 2372 + "tracing", 2373 + "url", 2374 + ] 2375 + 2376 + [[package]] 1804 2377 name = "stable_deref_trait" 1805 2378 version = "1.2.1" 1806 2379 source = "registry+https://github.com/rust-lang/crates.io-index" 1807 2380 checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 2381 + 2382 + [[package]] 2383 + name = "stringprep" 2384 + version = "0.1.5" 2385 + source = "registry+https://github.com/rust-lang/crates.io-index" 2386 + checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" 2387 + dependencies = [ 2388 + "unicode-bidi", 2389 + "unicode-normalization", 2390 + "unicode-properties", 2391 + ] 1808 2392 1809 2393 [[package]] 1810 2394 name = "strip-ansi-escapes" ··· 2010 2594 ] 2011 2595 2012 2596 [[package]] 2597 + name = "tinyvec" 2598 + version = "1.10.0" 2599 + source = "registry+https://github.com/rust-lang/crates.io-index" 2600 + checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" 2601 + dependencies = [ 2602 + "tinyvec_macros", 2603 + ] 2604 + 2605 + [[package]] 2606 + name = "tinyvec_macros" 2607 + version = "0.1.1" 2608 + source = "registry+https://github.com/rust-lang/crates.io-index" 2609 + checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 2610 + 2611 + [[package]] 2013 2612 name = "tokio" 2014 2613 version = "1.48.0" 2015 2614 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2038 2637 ] 2039 2638 2040 2639 [[package]] 2640 + name = "tokio-stream" 2641 + version = "0.1.17" 2642 + source = "registry+https://github.com/rust-lang/crates.io-index" 2643 + checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" 2644 + dependencies = [ 2645 + "futures-core", 2646 + "pin-project-lite", 2647 + "tokio", 2648 + ] 2649 + 2650 + [[package]] 2041 2651 name = "tokio-util" 2042 2652 version = "0.7.17" 2043 2653 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2086 2696 source = "registry+https://github.com/rust-lang/crates.io-index" 2087 2697 checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 2088 2698 dependencies = [ 2699 + "log", 2089 2700 "pin-project-lite", 2090 2701 "tracing-attributes", 2091 2702 "tracing-core", ··· 2144 2755 checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 2145 2756 2146 2757 [[package]] 2758 + name = "unicode-bidi" 2759 + version = "0.3.18" 2760 + source = "registry+https://github.com/rust-lang/crates.io-index" 2761 + checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" 2762 + 2763 + [[package]] 2147 2764 name = "unicode-ident" 2148 2765 version = "1.0.18" 2149 2766 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2154 2771 version = "0.1.5" 2155 2772 source = "registry+https://github.com/rust-lang/crates.io-index" 2156 2773 checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" 2774 + 2775 + [[package]] 2776 + name = "unicode-normalization" 2777 + version = "0.1.25" 2778 + source = "registry+https://github.com/rust-lang/crates.io-index" 2779 + checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" 2780 + dependencies = [ 2781 + "tinyvec", 2782 + ] 2783 + 2784 + [[package]] 2785 + name = "unicode-properties" 2786 + version = "0.1.4" 2787 + source = "registry+https://github.com/rust-lang/crates.io-index" 2788 + checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" 2157 2789 2158 2790 [[package]] 2159 2791 name = "unicode-width" ··· 2204 2836 checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 2205 2837 2206 2838 [[package]] 2839 + name = "vcpkg" 2840 + version = "0.2.15" 2841 + source = "registry+https://github.com/rust-lang/crates.io-index" 2842 + checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 2843 + 2844 + [[package]] 2207 2845 name = "version_check" 2208 2846 version = "0.9.5" 2209 2847 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2234 2872 ] 2235 2873 2236 2874 [[package]] 2875 + name = "wasite" 2876 + version = "0.1.0" 2877 + source = "registry+https://github.com/rust-lang/crates.io-index" 2878 + checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" 2879 + 2880 + [[package]] 2881 + name = "whoami" 2882 + version = "1.6.1" 2883 + source = "registry+https://github.com/rust-lang/crates.io-index" 2884 + checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" 2885 + dependencies = [ 2886 + "libredox", 2887 + "wasite", 2888 + ] 2889 + 2890 + [[package]] 2237 2891 name = "winapi" 2238 2892 version = "0.3.9" 2239 2893 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2269 2923 2270 2924 [[package]] 2271 2925 name = "windows-sys" 2926 + version = "0.48.0" 2927 + source = "registry+https://github.com/rust-lang/crates.io-index" 2928 + checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 2929 + dependencies = [ 2930 + "windows-targets 0.48.5", 2931 + ] 2932 + 2933 + [[package]] 2934 + name = "windows-sys" 2272 2935 version = "0.52.0" 2273 2936 source = "registry+https://github.com/rust-lang/crates.io-index" 2274 2937 checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" ··· 2305 2968 2306 2969 [[package]] 2307 2970 name = "windows-targets" 2971 + version = "0.48.5" 2972 + source = "registry+https://github.com/rust-lang/crates.io-index" 2973 + checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2974 + dependencies = [ 2975 + "windows_aarch64_gnullvm 0.48.5", 2976 + "windows_aarch64_msvc 0.48.5", 2977 + "windows_i686_gnu 0.48.5", 2978 + "windows_i686_msvc 0.48.5", 2979 + "windows_x86_64_gnu 0.48.5", 2980 + "windows_x86_64_gnullvm 0.48.5", 2981 + "windows_x86_64_msvc 0.48.5", 2982 + ] 2983 + 2984 + [[package]] 2985 + name = "windows-targets" 2308 2986 version = "0.52.6" 2309 2987 source = "registry+https://github.com/rust-lang/crates.io-index" 2310 2988 checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" ··· 2338 3016 2339 3017 [[package]] 2340 3018 name = "windows_aarch64_gnullvm" 3019 + version = "0.48.5" 3020 + source = "registry+https://github.com/rust-lang/crates.io-index" 3021 + checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 3022 + 3023 + [[package]] 3024 + name = "windows_aarch64_gnullvm" 2341 3025 version = "0.52.6" 2342 3026 source = "registry+https://github.com/rust-lang/crates.io-index" 2343 3027 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" ··· 2350 3034 2351 3035 [[package]] 2352 3036 name = "windows_aarch64_msvc" 3037 + version = "0.48.5" 3038 + source = "registry+https://github.com/rust-lang/crates.io-index" 3039 + checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 3040 + 3041 + [[package]] 3042 + name = "windows_aarch64_msvc" 2353 3043 version = "0.52.6" 2354 3044 source = "registry+https://github.com/rust-lang/crates.io-index" 2355 3045 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" ··· 2362 3052 2363 3053 [[package]] 2364 3054 name = "windows_i686_gnu" 3055 + version = "0.48.5" 3056 + source = "registry+https://github.com/rust-lang/crates.io-index" 3057 + checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 3058 + 3059 + [[package]] 3060 + name = "windows_i686_gnu" 2365 3061 version = "0.52.6" 2366 3062 source = "registry+https://github.com/rust-lang/crates.io-index" 2367 3063 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" ··· 2383 3079 version = "0.53.0" 2384 3080 source = "registry+https://github.com/rust-lang/crates.io-index" 2385 3081 checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 3082 + 3083 + [[package]] 3084 + name = "windows_i686_msvc" 3085 + version = "0.48.5" 3086 + source = "registry+https://github.com/rust-lang/crates.io-index" 3087 + checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2386 3088 2387 3089 [[package]] 2388 3090 name = "windows_i686_msvc" ··· 2398 3100 2399 3101 [[package]] 2400 3102 name = "windows_x86_64_gnu" 3103 + version = "0.48.5" 3104 + source = "registry+https://github.com/rust-lang/crates.io-index" 3105 + checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 3106 + 3107 + [[package]] 3108 + name = "windows_x86_64_gnu" 2401 3109 version = "0.52.6" 2402 3110 source = "registry+https://github.com/rust-lang/crates.io-index" 2403 3111 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" ··· 2410 3118 2411 3119 [[package]] 2412 3120 name = "windows_x86_64_gnullvm" 3121 + version = "0.48.5" 3122 + source = "registry+https://github.com/rust-lang/crates.io-index" 3123 + checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 3124 + 3125 + [[package]] 3126 + name = "windows_x86_64_gnullvm" 2413 3127 version = "0.52.6" 2414 3128 source = "registry+https://github.com/rust-lang/crates.io-index" 2415 3129 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" ··· 2419 3133 version = "0.53.0" 2420 3134 source = "registry+https://github.com/rust-lang/crates.io-index" 2421 3135 checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 3136 + 3137 + [[package]] 3138 + name = "windows_x86_64_msvc" 3139 + version = "0.48.5" 3140 + source = "registry+https://github.com/rust-lang/crates.io-index" 3141 + checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2422 3142 2423 3143 [[package]] 2424 3144 name = "windows_x86_64_msvc" ··· 2595 3315 "quote", 2596 3316 "syn 2.0.111", 2597 3317 ] 3318 + 3319 + [[package]] 3320 + name = "zstd" 3321 + version = "0.13.3" 3322 + source = "registry+https://github.com/rust-lang/crates.io-index" 3323 + checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" 3324 + dependencies = [ 3325 + "zstd-safe", 3326 + ] 3327 + 3328 + [[package]] 3329 + name = "zstd-safe" 3330 + version = "7.2.4" 3331 + source = "registry+https://github.com/rust-lang/crates.io-index" 3332 + checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" 3333 + dependencies = [ 3334 + "zstd-sys", 3335 + ] 3336 + 3337 + [[package]] 3338 + name = "zstd-sys" 3339 + version = "2.0.16+zstd.1.5.7" 3340 + source = "registry+https://github.com/rust-lang/crates.io-index" 3341 + checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" 3342 + dependencies = [ 3343 + "cc", 3344 + "pkg-config", 3345 + ]
+3
Cargo.toml
··· 47 47 # ] } 48 48 serde_json = { version = "1.0.145" } 49 49 owo-colors = { version = "4.2.3", features = ["supports-colors"] } 50 + 51 + [profile.dev.package.sqlx-macros] 52 + opt-level = 3
+16 -4
bench/README.md
··· 4 4 5 5 The hive can be found in `default.nix`. 6 6 7 + Run the test with `nix run .#checks.x86_64-linux.bench.driverInteractive -vvv -L 8 + --show-trace --impure` 9 + 10 + Then run `test_script()` 11 + 12 + No idea why running the test directly breaks it.... 13 + 14 + You can adjust the number of nodes in `num-nodes.nix` 15 + 7 16 The hive has around 20 nodes and 200 keys each. 80% of the keys are pre-activation, 20% post-activation. 8 17 9 - | Command | Mean [s] | Min [s] | Max [s] | Relative | 10 - | :--------------- | ---------------: | ------: | ------: | ----------: | 11 - | `colmena@pinned` | 301.977 ± 17.026 | 288.432 | 321.090 | 2.51 ± 0.35 | 12 - | `wire@HEAD` | 120.123 ± 15.044 | 110.539 | 137.462 | 1.00 | 18 + | Command | Mean [s] | Min [s] | Max [s] | Relative | 19 + | :----------------------- | --------------: | ------: | ------: | ----------: | 20 + | `wire@HEAD - flake` | 89.825 ± 22.941 | 78.190 | 130.831 | 1.00 | 21 + | `wire@stable - flake` | 133.664 ± 0.303 | 133.219 | 134.044 | 1.49 ± 0.38 | 22 + | `colmena@pinned - flake` | 131.544 ± 1.076 | 130.330 | 133.211 | 1.46 ± 0.37 | 23 + | `wire@stable - hive.nix` | 133.070 ± 0.805 | 132.166 | 134.209 | 1.48 ± 0.38 | 24 + | `wire@HEAD - hive.nix` | 130.287 ± 1.456 | 128.980 | 132.699 | 1.45 ± 0.37 |
+11
bench/colmena-flake/flake.nix
··· 1 + { 2 + inputs.wire.url = "git+file:///root/wire"; 3 + 4 + outputs = 5 + { wire, ... }: 6 + { 7 + colmenaHive = wire.inputs.colmena_benchmarking.lib.makeHive ( 8 + import "${wire}/bench/default.nix" { flake = wire; } 9 + ); 10 + }; 11 + }
+6 -8
bench/default.nix
··· 1 1 { flake }: 2 2 let 3 - nixpkgs = import flake.inputs.nixpkgs { }; 3 + nixpkgs = import flake.inputs.nixpkgs { 4 + system = "x86_64-linux"; 5 + }; 4 6 5 7 vmNode = 6 8 index: 7 - nixpkgs.lib.nameValuePair "bench-vm-${builtins.toString index}" { 8 - deployment = { 9 - targetPort = 2000 + index; 10 - targetHost = "localhost"; 11 - }; 12 - 9 + nixpkgs.lib.nameValuePair "node_${builtins.toString index}" { 13 10 imports = [ 14 11 ./vm.nix 12 + flake.checks."x86_64-linux"."bench".nodes."node_${builtins.toString index}".system.build.networkConfig 15 13 ]; 16 14 17 15 _module.args = { ··· 35 33 { 36 34 meta.nixpkgs = nixpkgs; 37 35 } 38 - // builtins.listToAttrs (builtins.map vmNode (nixpkgs.lib.range 0 20)) 36 + // builtins.listToAttrs (builtins.map vmNode (nixpkgs.lib.range 0 (import ./num-nodes.nix)))
+1
bench/num-nodes.nix
··· 1 + 1
-50
bench/run.nix
··· 1 - { inputs, ... }: 2 - { 3 - perSystem = 4 - { 5 - pkgs, 6 - lib, 7 - self', 8 - ... 9 - }: 10 - { 11 - packages = { 12 - bench-runner = pkgs.writeShellScriptBin "bench-runner" '' 13 - set -e 14 - 15 - export NIX_PATH="nixpkgs=${inputs.nixpkgs}" 16 - 17 - setup_vm() { 18 - local i="$1" 19 - 20 - echo "building $i" 21 - 22 - out=$(INDEX="$i" nix-build '<nixpkgs/nixos>' -A vm -I nixos-config=./vm.nix --no-link) 23 - 24 - echo "$out/bin/run-bench-vm-$i-vm" 25 - } 26 - 27 - export -f setup_vm 28 - 29 - echo "setting up vms..." 30 - ${lib.getExe pkgs.parallel-full} --verbose --tagstring {//} setup_vm {} ::: {0..20} | xargs -I {} bash -c '{} &' 31 - 32 - echo "sleeping" 33 - sleep 30 34 - echo "awake" 35 - 36 - wire_trunk=$(nix build --print-out-paths github:mrshmllow/wire#wire-small --no-link) 37 - wire_args="apply test --path ./wire -vv --ssh-accept-host -p 10" 38 - colmena_args="apply test --config ./colmena/hive.nix -v -p 10" 39 - 40 - ${lib.getExe pkgs.hyperfine} --warmup 1 --show-output --runs 1 \ 41 - --export-markdown stats.md \ 42 - --export-json run.json \ 43 - "${lib.getExe self'.packages.wire-small} $wire_args" -n "wire@HEAD" \ 44 - "$wire_trunk/bin/wire $wire_args" -n "wire@trunk" \ 45 - "${lib.getExe' inputs.colmena_benchmarking.packages.x86_64-linux.colmena "colmena"} $colmena_args" \ 46 - -n "colmena@pinned" 47 - ''; 48 - }; 49 - }; 50 - }
+171
bench/runner.nix
··· 1 + # SPDX-License-Identifier: AGPL-3.0-or-later 2 + # Copyright 2024-2025 wire Contributors 3 + 4 + { 5 + lib, 6 + inputs, 7 + ... 8 + }: 9 + let 10 + inherit (lib) 11 + mapAttrsToList 12 + flatten 13 + ; 14 + in 15 + { 16 + config.perSystem = 17 + { 18 + pkgs, 19 + self', 20 + system, 21 + ... 22 + }: 23 + let 24 + benchDirFileset = lib.fileset.toSource { 25 + root = ../..; 26 + fileset = lib.fileset.union ./. ( 27 + lib.fileset.fileFilter ( 28 + file: (file.hasExt "nix") || (file.hasExt "txt") || (file.hasExt "lock") 29 + ) ../. 30 + ); 31 + }; 32 + 33 + nodes = 34 + builtins.listToAttrs ( 35 + builtins.map (index: { 36 + value = { 37 + imports = [ 38 + ./vm.nix 39 + ]; 40 + 41 + _module.args = { 42 + index = builtins.toString index; 43 + }; 44 + }; 45 + name = "node_${builtins.toString index}"; 46 + }) (lib.range 0 (import ./num-nodes.nix)) 47 + ) 48 + // { 49 + deployer = { 50 + imports = [ 51 + ./vm.nix 52 + ]; 53 + 54 + environment.systemPackages = [ 55 + pkgs.git 56 + 57 + (pkgs.writeShellScriptBin "setup-benchmark" '' 58 + mkdir -p $HOME/wire 59 + cp -r ${benchDirFileset}/*-source/* $HOME/wire 60 + 61 + cp -r $HOME/wire/bench/wire-flake $HOME/wire-flake 62 + cp -r $HOME/wire/bench/colmena-flake $HOME/colmena-flake 63 + 64 + chmod -R +w $HOME/* 65 + 66 + cd $HOME/wire 67 + git init . 68 + git add -A 69 + '') 70 + 71 + (pkgs.writeShellScriptBin "run-benchmark" '' 72 + bench_dir=$HOME/wire/bench 73 + 74 + wire_args="apply test --path $bench_dir/wire -vv --ssh-accept-host -p 10" 75 + wire_args_flake="apply test --path $HOME/wire-flake -vv --ssh-accept-host -p 10" 76 + 77 + colmena_args="apply test --config $bench_dir/colmena/hive.nix -v -p 10" 78 + colmena_args_flake="apply test --config $HOME/colmena-flake/flake.nix -v -p 10" 79 + 80 + ${lib.getExe pkgs.hyperfine} --warmup 1 --show-output --runs 5 \ 81 + --export-markdown stats.md \ 82 + --export-json run.json \ 83 + "${lib.getExe self'.packages.wire-small} $wire_args_flake" -n "wire@HEAD - flake" \ 84 + "${lib.getExe' inputs.colmena_benchmarking.packages.x86_64-linux.colmena "colmena"} $colmena_args_flake" \ 85 + -n "colmena@pinned - flake" \ 86 + "${lib.getExe self'.packages.wire-small} $wire_args" -n "wire@HEAD - hive.nix" 87 + '') 88 + ]; 89 + 90 + _module.args = { 91 + index = "deployer"; 92 + }; 93 + }; 94 + }; 95 + 96 + evalConfig = import (pkgs.path + "/nixos/lib/eval-config.nix"); 97 + 98 + evalVM = 99 + module: 100 + evalConfig { 101 + inherit system; 102 + modules = [ module ]; 103 + }; 104 + in 105 + { 106 + checks.bench = pkgs.testers.runNixOSTest { 107 + inherit nodes; 108 + 109 + name = "benchmark"; 110 + 111 + defaults = 112 + { 113 + ... 114 + }: 115 + let 116 + # hive = builtins.scopedImport { 117 + # __nixPath = _b: null; 118 + # __findFile = _path: name: if name == "nixpkgs" then pkgs.path else throw "oops!!"; 119 + # } "${injectedFlakeDir}/${path}/hive.nix"; 120 + 121 + # fetch **all** dependencies of a flake 122 + # it's called fetchLayer because my naming skills are awful 123 + fetchLayer = 124 + input: 125 + let 126 + subLayers = if input ? inputs then map fetchLayer (builtins.attrValues input.inputs) else [ ]; 127 + in 128 + [ 129 + input.outPath 130 + ] 131 + ++ subLayers; 132 + in 133 + { 134 + virtualisation.additionalPaths = flatten [ 135 + (mapAttrsToList (_: val: (evalVM val).config.system.build.toplevel.drvPath) nodes) 136 + (mapAttrsToList (_: fetchLayer) inputs) 137 + ]; 138 + 139 + nix.settings.experimental-features = [ 140 + "nix-command" 141 + "flakes" 142 + ]; 143 + }; 144 + node.specialArgs = { 145 + snakeOil = import "${pkgs.path}/nixos/tests/ssh-keys.nix" pkgs; 146 + inherit (self'.packages) wire-small-dev; 147 + }; 148 + skipTypeCheck = true; 149 + testScript = '' 150 + start_all() 151 + 152 + for i in range(0,${builtins.toString (import ./num-nodes.nix)}): 153 + machine = globals().get(f"node_{i}") 154 + machine.wait_for_unit("sshd.service") # type: ignore 155 + 156 + node_deployer.succeed("setup-benchmark"); 157 + node_deployer.succeed("run-benchmark"); 158 + 159 + node_deployer.copy_from_vm("run.json") 160 + node_deployer.copy_from_vm("stats.json") 161 + ''; 162 + }; 163 + }; 164 + } 165 + 166 + # "${ 167 + # lib.getExe (builtins.getFlake "github:mrshmllow/wire/stable").packages.${system}.wire-small 168 + # } $wire_args" -n "wire@stable - hive.nix" \ 169 + # "${ 170 + # lib.getExe (builtins.getFlake "github:mrshmllow/wire/stable").packages.${system}.wire-small 171 + # } $wire_args_flake" -n "wire@stable - flake" \
+33 -24
bench/vm.nix
··· 1 - { lib, index, ... }: 1 + { 2 + lib, 3 + index, 4 + modulesPath, 5 + pkgs, 6 + ... 7 + }: 2 8 let 3 9 flake = import ../default.nix; 10 + snakeOil = import "${pkgs.path}/nixos/tests/ssh-keys.nix" pkgs; 4 11 in 5 12 { 6 - _module.args = { 7 - index = lib.mkDefault (builtins.getEnv "INDEX"); 8 - }; 13 + imports = [ 14 + "${flake.inputs.nixpkgs}/nixos/modules/virtualisation/qemu-vm.nix" 15 + "${modulesPath}/virtualisation/qemu-vm.nix" 16 + "${modulesPath}/testing/test-instrumentation.nix" 17 + ]; 9 18 10 - imports = [ "${flake.inputs.nixpkgs}/nixos/modules/virtualisation/qemu-vm.nix" ]; 11 - 12 - networking.hostName = "bench-vm-${index}"; 19 + networking.hostName = "node_${index}"; 13 20 14 21 boot = { 15 22 loader = { 16 23 systemd-boot.enable = true; 17 24 efi.canTouchEfiVariables = true; 18 - timeout = 0; 19 25 }; 26 + }; 20 27 21 - kernelParams = [ "console=ttyS0" ]; 22 - }; 28 + environment.variables.XDG_RUNTIME_DIR = "/tmp"; 23 29 24 30 services = { 25 31 openssh = { ··· 37 43 # useBootLoader = true; 38 44 39 45 diskSize = 5024; 40 - diskImage = null; 46 + memorySize = 4096; 47 + }; 41 48 42 - forwardPorts = [ 43 - { 44 - from = "host"; 45 - host.port = 2000 + lib.toIntBase10 index; 46 - guest.port = 22; 47 - } 48 - ]; 49 - }; 49 + # It's important to note that you should never ever use this configuration 50 + # for production. You are risking a MITM attack with this! 51 + programs.ssh.extraConfig = '' 52 + Host * 53 + StrictHostKeyChecking no 54 + UserKnownHostsFile /dev/null 55 + ''; 50 56 51 - users.users.root.openssh.authorizedKeys.keys = [ 52 - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPSvOZoSGVEpR6eTDK9OJ31MWQPF2s8oLc8J7MBh6nez marsh@maple" 57 + users.users.root.openssh.authorizedKeys.keys = [ snakeOil.snakeOilEd25519PublicKey ]; 58 + systemd.tmpfiles.rules = [ 59 + "C+ /root/.ssh/id_ed25519 600 - - - ${snakeOil.snakeOilEd25519PrivateKey}" 53 60 ]; 54 61 55 - users.users.root.initialPassword = "root"; 56 - 57 - system.stateVersion = "23.11"; 62 + nix = { 63 + nixPath = [ "nixpkgs=${pkgs.path}" ]; 64 + settings.substituters = lib.mkForce [ ]; 65 + package = pkgs.lix; 66 + }; 58 67 }
+9
bench/wire-flake/flake.nix
··· 1 + { 2 + inputs.wire.url = "git+file:///root/wire"; 3 + 4 + outputs = 5 + { wire, ... }: 6 + { 7 + wire = wire.makeHive (import "${wire}/bench/default.nix" { flake = wire; }); 8 + }; 9 + }
+1 -1
doc/guides/migrate.md
··· 47 47 { nixpkgs, colmena, ... }: 48 48 { 49 49 colmenaHive = colmena.lib.makeHive { # [!code --] 50 - wire = colmena.lib.makeHive { # [!code ++] 50 + wire = wire.lib.makeHive { # [!code ++] 51 51 # .. 52 52 }; 53 53 };
+4 -4
doc/tutorial/overview.md
··· 56 56 wire is about >2x faster than colmena deploying [identical large 57 57 hives](https://github.com/mrshmllow/wire/blob/trunk/bench/run.nix). 58 58 59 - | Command | Mean [s] | Min [s] | Max [s] | Relative | 60 - | :--------------- | ---------------: | ------: | ------: | ----------: | 61 - | `colmena@pinned` | 301.977 ± 17.026 | 288.432 | 321.090 | 2.51 ± 0.35 | 62 - | `wire@HEAD` | 120.123 ± 15.044 | 110.539 | 137.462 | 1.00 | 59 + | Command | Mean [s] | Min [s] | Max [s] | Relative | 60 + | :--------------- | -------: | ------: | ------: | -------: | 61 + | `wire@HEAD` | 185.075 | 185.075 | 185.075 | 1.00 | 62 + | `colmena@pinned` | 343.075 | 343.075 | 343.075 | 1.85 |
+2 -1
flake.nix
··· 41 41 ./wire/key_agent 42 42 ./doc 43 43 ./tests/nix 44 - ./bench/run.nix 45 44 ./runtime 45 + ./bench/runner.nix 46 46 ]; 47 47 systems = import systems; 48 48 ··· 93 93 }; 94 94 settings.formatter = { 95 95 nixfmt.excludes = [ "doc/snippets/*.nix" ]; 96 + prettier.excludes = [ ".sqlx/*" ]; 96 97 alejandra = { 97 98 includes = lib.mkForce [ "doc/snippets/*.nix" ]; 98 99 };
+4
nix/shells.nix
··· 21 21 pkgs.just 22 22 pkgs.pnpm 23 23 pkgs.nodejs 24 + pkgs.sqlx-cli 25 + pkgs.sqlite 26 + pkgs.limbo 27 + pkgs.zstd 24 28 ]; 25 29 26 30 PROTOC = lib.getExe pkgs.protobuf;
+1
nix/utils.nix
··· 19 19 root = ../.; 20 20 fileset = unions [ 21 21 ../.cargo 22 + ../.sqlx 22 23 ../wire 23 24 ../Cargo.toml 24 25 ../Cargo.lock
+2
runtime/evaluate.nix
··· 104 104 105 105 nodes = builtins.mapAttrs (_: v: v.config.deployment) nodes; 106 106 }; 107 + 108 + names = nodeNames; 107 109 }
+1 -1
wire/cli/Cargo.toml
··· 28 28 clap-markdown = "0.1.5" 29 29 itertools = "0.14.0" 30 30 dhat = "0.3.2" 31 - clap_complete = "4.5.60" 31 + clap_complete = { version = "4.5.60", features = ["unstable-dynamic"] } 32 32 nix-compat = { workspace = true } 33 33 owo-colors = { workspace = true }
+9 -3
wire/cli/default.nix
··· 25 25 doCheck = true; 26 26 nativeBuildInputs = [ 27 27 pkgs.installShellFiles 28 + pkgs.sqlx-cli 28 29 ]; 30 + preBuild = '' 31 + export DATABASE_URL=sqlite:./db.sqlite3 32 + sqlx database create 33 + sqlx migrate run --source ./wire/lib/src/cache/migrations/ 34 + ''; 29 35 postInstall = '' 30 36 installShellCompletion --cmd wire \ 31 - --bash <($out/bin/wire completions bash) \ 32 - --fish <($out/bin/wire completions fish) \ 33 - --zsh <($out/bin/wire completions zsh) 37 + --bash <(COMPLETE=bash $out/bin/wire) \ 38 + --fish <(COMPLETE=fish $out/bin/wire) \ 39 + --zsh <(COMPLETE=zsh $out/bin/wire) 34 40 ''; 35 41 }; 36 42
+64 -12
wire/cli/src/cli.rs
··· 2 2 // Copyright 2024-2025 wire Contributors 3 3 4 4 use clap::builder::PossibleValue; 5 - use clap::crate_version; 6 5 use clap::{Args, Parser, Subcommand, ValueEnum}; 7 - use clap_complete::Shell; 6 + use clap::{ValueHint, crate_version}; 7 + use clap_complete::CompletionCandidate; 8 + use clap_complete::engine::ArgValueCompleter; 8 9 use clap_num::number_range; 9 10 use clap_verbosity_flag::InfoLevel; 10 11 use lib::SubCommandModifiers; 11 - use lib::hive::Hive; 12 + use lib::commands::common::get_hive_node_names; 12 13 use lib::hive::node::{Goal as HiveGoal, HandleUnreachable, Name, SwitchToConfigurationGoal}; 14 + use lib::hive::{Hive, get_hive_location}; 15 + use tokio::runtime::Handle; 13 16 14 17 use std::io::IsTerminal; 15 18 use std::{ ··· 36 39 #[arg(long, global = true, default_value = std::env::current_dir().unwrap().into_os_string(), visible_alias("flake"))] 37 40 pub path: String, 38 41 39 - // Unused until a solution to tracing-indicatif log deadlocking is found... 40 42 /// Hide progress bars. 41 43 /// 42 44 /// Defaults to true if stdin does not refer to a tty (unix pipelines, in CI). ··· 139 141 /// 140 142 /// `-` will read additional values from stdin, separated by whitespace. 141 143 /// Any `-` implies `--non-interactive`. 142 - #[arg(short, long, value_name = "NODE | @TAG | `-`", num_args = 1..)] 144 + #[arg(short, long, value_name = "NODE | @TAG | `-`", num_args = 1.., add = ArgValueCompleter::new(node_names_completer), value_hint = ValueHint::Unknown)] 143 145 pub on: Vec<ApplyTarget>, 144 146 145 147 #[arg(short, long, default_value_t = 10, value_parser=more_than_zero)] ··· 179 181 /// Inspect hive 180 182 #[clap(visible_alias = "show")] 181 183 Inspect { 184 + #[arg(value_enum, default_value_t)] 185 + selection: Inspection, 186 + 182 187 /// Return in JSON format 183 188 #[arg(short, long, default_value_t = false)] 184 189 json: bool, 185 190 }, 186 - /// Generates shell completions 187 - #[clap(hide = true)] 188 - Completions { 189 - #[arg()] 190 - // Shell to generate completions for 191 - shell: Shell, 192 - }, 191 + } 192 + 193 + #[derive(Clone, Debug, Default, ValueEnum, Display)] 194 + pub enum Inspection { 195 + /// Output all data wire has on the entire hive 196 + #[default] 197 + Full, 198 + /// Only output a list of node names 199 + Names, 193 200 } 194 201 195 202 #[derive(Clone, Debug, Default, ValueEnum, Display)] ··· 251 258 } 252 259 } 253 260 } 261 + 262 + fn node_names_completer(current: &std::ffi::OsStr) -> Vec<CompletionCandidate> { 263 + tokio::task::block_in_place(|| { 264 + let handle = Handle::current(); 265 + let modifiers = SubCommandModifiers::default(); 266 + let mut completions = vec![]; 267 + 268 + if current.is_empty() || current == "-" { 269 + completions.push( 270 + CompletionCandidate::new("-").help(Some("Read stdin as --on arguments".into())), 271 + ); 272 + } 273 + 274 + let Ok(current_dir) = std::env::current_dir() else { 275 + return completions; 276 + }; 277 + 278 + let Ok(hive_location) = handle.block_on(get_hive_location( 279 + current_dir.display().to_string(), 280 + modifiers, 281 + )) else { 282 + return completions; 283 + }; 284 + 285 + let Some(current) = current.to_str() else { 286 + return completions; 287 + }; 288 + 289 + if current.starts_with('@') { 290 + return vec![]; 291 + } 292 + 293 + if let Ok(names) = 294 + handle.block_on(async { get_hive_node_names(&hive_location, modifiers).await }) 295 + { 296 + for name in names { 297 + if name.starts_with(current) { 298 + completions.push(CompletionCandidate::new(name)); 299 + } 300 + } 301 + } 302 + 303 + completions 304 + }) 305 + }
+32 -17
wire/cli/src/main.rs
··· 11 11 use crate::tracing_setup::setup_logging; 12 12 use clap::CommandFactory; 13 13 use clap::Parser; 14 - use clap_complete::generate; 14 + use clap_complete::CompleteEnv; 15 + use lib::cache::InspectionCache; 16 + use lib::commands::common::get_hive_node_names; 15 17 use lib::hive::Hive; 16 18 use lib::hive::get_hive_location; 17 19 use miette::IntoDiagnostic; ··· 34 36 async fn main() -> Result<()> { 35 37 #[cfg(feature = "dhat-heap")] 36 38 let _profiler = dhat::Profiler::new_heap(); 39 + CompleteEnv::with_factory(Cli::command).complete(); 37 40 38 41 let args = Cli::parse(); 39 42 40 43 let modifiers = args.to_subcommand_modifiers(); 41 - setup_logging(&args.verbose, !&args.no_progress); 44 + // disable progress when running inspect mode. 45 + setup_logging( 46 + &args.verbose, 47 + !matches!(args.command, cli::Commands::Inspect { .. }) && !&args.no_progress, 48 + ); 42 49 43 50 #[cfg(debug_assertions)] 44 51 if args.markdown_help { ··· 46 53 return Ok(()); 47 54 } 48 55 49 - if !matches!(args.command, cli::Commands::Completions { .. }) && !check_nix_available() { 56 + if !check_nix_available() { 50 57 miette::bail!("Nix is not available on this system."); 51 58 } 52 59 53 - let location = get_hive_location(args.path)?; 60 + let location = get_hive_location(args.path, modifiers).await?; 61 + let cache = InspectionCache::new().await; 54 62 55 63 match args.command { 56 64 cli::Commands::Apply(apply_args) => { 57 - let mut hive = Hive::new_from_path(&location, modifiers).await?; 65 + let mut hive = Hive::new_from_path(&location, cache.clone(), modifiers).await?; 58 66 apply::apply(&mut hive, location, apply_args, modifiers).await?; 59 67 } 60 - cli::Commands::Inspect { json } => println!("{}", { 61 - let hive = Hive::new_from_path(&location, modifiers).await?; 62 - if json { 63 - serde_json::to_string(&hive).into_diagnostic()? 64 - } else { 65 - warn!("use --json to output something scripting suitable"); 66 - format!("{hive}") 68 + cli::Commands::Inspect { json, selection } => println!("{}", { 69 + match selection { 70 + cli::Inspection::Full => { 71 + let hive = Hive::new_from_path(&location, cache.clone(), modifiers).await?; 72 + if json { 73 + serde_json::to_string(&hive).into_diagnostic()? 74 + } else { 75 + warn!("use --json to output something scripting suitable"); 76 + format!("{hive}") 77 + } 78 + } 79 + cli::Inspection::Names => { 80 + serde_json::to_string(&get_hive_node_names(&location, modifiers).await?) 81 + .into_diagnostic()? 82 + } 67 83 } 68 84 }), 69 - cli::Commands::Completions { shell } => { 70 - let mut cmd = Cli::command(); 71 - let name = cmd.clone(); 72 - generate(shell, &mut cmd, name.get_name(), &mut std::io::stdout()); 73 - } 85 + } 86 + 87 + if let Some(cache) = cache { 88 + cache.gc().await.into_diagnostic()?; 74 89 } 75 90 76 91 Ok(())
+2
wire/lib/Cargo.toml
··· 38 38 gjson = "0.8.1" 39 39 owo-colors = { workspace = true } 40 40 termion = "4.0.6" 41 + sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite"] } 42 + zstd = "0.13.3" 41 43 42 44 [dev-dependencies] 43 45 tempdir = "0.3"
+13
wire/lib/src/cache/migrations/20251124234730_init.sql
··· 1 + create table hive_inspection ( 2 + id integer primary key autoincrement, 3 + json_value text not null unique 4 + ) strict; 5 + 6 + create table cached_inspection ( 7 + store_path text, 8 + hash text, 9 + 10 + inspection_id integer references hive_inspection(id) not null, 11 + 12 + primary key (store_path, hash) 13 + ) strict;
+16
wire/lib/src/cache/migrations/20251126222409_blobs.sql
··· 1 + create table inspection_blobs ( 2 + id integer primary key autoincrement, 3 + json_value blob not null unique, 4 + schema_version integer not null 5 + ) strict; 6 + 7 + create table inspection_cache ( 8 + store_path text, 9 + hash text, 10 + blob_id integer references inspection_blobs (id) not null, 11 + primary key (store_path, hash) 12 + ) strict; 13 + 14 + drop table cached_inspection; 15 + 16 + drop table hive_inspection;
+205
wire/lib/src/cache/mod.rs
··· 1 + // SPDX-License-Identifier: AGPL-3.0-or-later 2 + // Copyright 2024-2025 wire Contributors 3 + 4 + use std::{env, path::PathBuf}; 5 + 6 + use sqlx::{ 7 + Pool, Sqlite, 8 + migrate::Migrator, 9 + sqlite::{SqliteConnectOptions, SqlitePoolOptions}, 10 + }; 11 + use tokio::fs::create_dir_all; 12 + use tracing::{debug, error, trace}; 13 + 14 + use crate::hive::{FlakePrefetch, Hive}; 15 + 16 + #[derive(Clone)] 17 + pub struct InspectionCache { 18 + pool: Pool<Sqlite>, 19 + } 20 + 21 + static MIGRATOR: Migrator = sqlx::migrate!("src/cache/migrations"); 22 + 23 + async fn get_cache_directory() -> Option<PathBuf> { 24 + let home = PathBuf::from( 25 + env::var("HOME") 26 + .inspect_err(|_| error!("HOME env var not found")) 27 + .ok()?, 28 + ); 29 + 30 + trace!(home = ?home); 31 + 32 + let cache_home = env::var("XDG_CACHE_HOME") 33 + .inspect_err(|_| debug!("XDG_CACHE_HOME not found")) 34 + .ok() 35 + .map(PathBuf::from) 36 + .unwrap_or(home.join(".cache")); 37 + 38 + let cache_directory = cache_home.join("wire"); 39 + 40 + trace!(cache_directory = ?cache_directory); 41 + 42 + let _ = create_dir_all(&cache_directory).await; 43 + 44 + Some(cache_directory) 45 + } 46 + 47 + impl InspectionCache { 48 + pub async fn new() -> Option<Self> { 49 + let cache_path = get_cache_directory().await?.join("inspect.db"); 50 + debug!(cache_path = ?cache_path); 51 + 52 + let pool = SqlitePoolOptions::new() 53 + .max_connections(1) 54 + .connect_with( 55 + SqliteConnectOptions::new() 56 + .filename(cache_path) 57 + .create_if_missing(true), 58 + ) 59 + .await 60 + .inspect_err(|x| error!("failed to open cache db: {x}")) 61 + .ok()?; 62 + 63 + MIGRATOR 64 + .run(&pool) 65 + .await 66 + .inspect_err(|err| error!("failed to run cache migrations: {err:?}")) 67 + .ok()?; 68 + 69 + Some(Self { pool }) 70 + } 71 + 72 + pub async fn get_hive(&self, prefetch: &FlakePrefetch) -> Option<Hive> { 73 + let cached_blob: Vec<u8> = sqlx::query_scalar!( 74 + " 75 + select inspection_blobs.json_value from inspection_blobs 76 + join inspection_cache 77 + on inspection_cache.blob_id = inspection_blobs.id 78 + where inspection_cache.store_path = $1 79 + and inspection_cache.hash = $2 80 + and inspection_blobs.schema_version = $3 81 + limit 1 82 + ", 83 + prefetch.store_path, 84 + prefetch.hash, 85 + Hive::SCHEMA_VERSION 86 + ) 87 + .fetch_optional(&self.pool) 88 + .await 89 + .inspect_err(|x| error!("failed to fetch cached hive: {x}")) 90 + .ok()??; 91 + 92 + trace!("read {} bytes of zstd data from cache", cached_blob.len()); 93 + 94 + let json_string = zstd::decode_all(cached_blob.as_slice()) 95 + .inspect_err(|err| error!("failed to decode cached zstd data: {err}")) 96 + .ok()?; 97 + 98 + trace!( 99 + "inflated {} > {} in decoding", 100 + cached_blob.len(), 101 + json_string.len() 102 + ); 103 + 104 + serde_json::from_slice(&json_string) 105 + .inspect_err(|err| { 106 + error!("could not use cached evaluation: {err}"); 107 + }) 108 + .ok() 109 + } 110 + 111 + pub async fn store_hive(&self, prefetch: &FlakePrefetch, json_value: &String) { 112 + let Ok(json_value) = zstd::encode_all(json_value.as_bytes(), 0) 113 + .inspect_err(|err| error!("failed to encode data w/ zstd: {err}")) 114 + else { 115 + return; 116 + }; 117 + 118 + let hive_inspection = sqlx::query_scalar!( 119 + " 120 + insert into inspection_blobs (json_value, schema_version) 121 + values ($1, $2) 122 + on conflict(json_value) 123 + do update set json_value = excluded.json_value 124 + returning inspection_blobs.id 125 + ", 126 + json_value, 127 + Hive::SCHEMA_VERSION 128 + ) 129 + .fetch_one(&self.pool) 130 + .await 131 + .inspect_err(|x| error!("could not insert hive_inspection: {x}")); 132 + 133 + let Ok(blob_id) = hive_inspection else { 134 + return; 135 + }; 136 + 137 + let cached_inspection = sqlx::query!( 138 + " 139 + insert into 140 + inspection_cache (store_path, hash, blob_id) 141 + values 142 + ($1, $2, $3) 143 + ", 144 + prefetch.store_path, 145 + prefetch.hash, 146 + blob_id 147 + ) 148 + .execute(&self.pool) 149 + .await; 150 + 151 + if let Err(err) = cached_inspection { 152 + error!("could not insert cached_inspection: {err}"); 153 + } 154 + } 155 + 156 + pub async fn gc(&self) -> Result<(), sqlx::Error> { 157 + // keep newest 30 AND 158 + // delete caches that refer to a blob w/ wrong schmea 159 + sqlx::query!( 160 + "delete from inspection_cache 161 + where 162 + blob_id in ( 163 + select 164 + id 165 + from 166 + inspection_blobs 167 + where 168 + schema_version != $1 169 + ) 170 + or ROWID in ( 171 + select 172 + ROWID 173 + from 174 + inspection_cache 175 + order by 176 + ROWID desc 177 + limit 178 + -1 179 + offset 180 + 30 181 + )", 182 + Hive::SCHEMA_VERSION 183 + ) 184 + .execute(&self.pool) 185 + .await?; 186 + 187 + // delete orphaned blobs 188 + sqlx::query!( 189 + "delete from inspection_blobs 190 + where 191 + not exists ( 192 + select 193 + 1 194 + from 195 + inspection_cache 196 + where 197 + inspection_cache.blob_id = inspection_blobs.id 198 + )" 199 + ) 200 + .execute(&self.pool) 201 + .await?; 202 + 203 + Ok(()) 204 + } 205 + }
+14 -2
wire/lib/src/commands/common.rs
··· 8 8 use crate::{ 9 9 EvalGoal, SubCommandModifiers, 10 10 commands::{CommandArguments, Either, WireCommandChip, run_command, run_command_with_env}, 11 - errors::{CommandError, HiveLibError}, 11 + errors::{CommandError, HiveInitialisationError, HiveLibError}, 12 12 hive::{ 13 13 HiveLocation, 14 14 node::{Context, Push}, ··· 83 83 } 84 84 } 85 85 86 + pub async fn get_hive_node_names( 87 + location: &HiveLocation, 88 + modifiers: SubCommandModifiers, 89 + ) -> Result<Vec<String>, HiveLibError> { 90 + let output = evaluate_hive_attribute(location, &EvalGoal::Names, modifiers).await?; 91 + serde_json::from_str(&output).map_err(|err| { 92 + HiveLibError::HiveInitialisationError(HiveInitialisationError::ParseEvaluateError(err)) 93 + }) 94 + } 95 + 86 96 /// Evaluates the hive in flakeref with regards to the given goal, 87 97 /// and returns stdout. 88 98 #[instrument(ret(level = tracing::Level::TRACE), skip_all)] ··· 92 102 modifiers: SubCommandModifiers, 93 103 ) -> Result<String, HiveLibError> { 94 104 let attribute = match location { 95 - HiveLocation::Flake(uri) => { 105 + HiveLocation::Flake { uri, .. } => { 96 106 format!( 97 107 "{uri}#wire --apply \"hive: {}\"", 98 108 match goal { 99 109 EvalGoal::Inspect => "hive.inspect".to_string(), 110 + EvalGoal::Names => "hive.names".to_string(), 100 111 EvalGoal::GetTopLevel(node) => format!("hive.topLevels.{node}"), 101 112 } 102 113 ) ··· 107 118 &path.to_string_lossy(), 108 119 match goal { 109 120 EvalGoal::Inspect => "inspect".to_string(), 121 + EvalGoal::Names => "names".to_string(), 110 122 EvalGoal::GetTopLevel(node) => format!("topLevels.{node}"), 111 123 } 112 124 )
+1 -1
wire/lib/src/commands/mod.rs
··· 18 18 hive::node::{Node, Target}, 19 19 }; 20 20 21 - pub(crate) mod common; 21 + pub mod common; 22 22 pub(crate) mod noninteractive; 23 23 pub(crate) mod pty; 24 24
+8
wire/lib/src/errors.rs
··· 141 141 ParseEvaluateError(#[source] serde_json::Error), 142 142 143 143 #[diagnostic( 144 + code(wire::hive_init::ParsePrefetch), 145 + help("please create an issue."), 146 + url("{DOCS_URL}#{}", self.code().unwrap()) 147 + )] 148 + #[error("Failed to parse `nix flake prefetch --json`.")] 149 + ParsePrefetchError(#[source] serde_json::Error), 150 + 151 + #[diagnostic( 144 152 code(wire::hive_init::NodeDoesNotExist), 145 153 help("Please create an issue!"), 146 154 url("{DOCS_URL}#{}", self.code().unwrap())
+94 -21
wire/lib/src/hive/mod.rs
··· 15 15 use std::path::PathBuf; 16 16 use std::str::FromStr; 17 17 use std::sync::Arc; 18 - use tracing::{info, instrument}; 18 + use tracing::{debug, info, instrument}; 19 19 20 + use crate::cache::InspectionCache; 20 21 use crate::commands::common::evaluate_hive_attribute; 22 + use crate::commands::{CommandArguments, Either, WireCommandChip, run_command}; 21 23 use crate::errors::{HiveInitialisationError, HiveLocationError}; 22 24 use crate::{EvalGoal, HiveLibError, SubCommandModifiers}; 23 25 pub mod node; ··· 53 55 #[instrument(skip_all, name = "eval_hive")] 54 56 pub async fn new_from_path( 55 57 location: &HiveLocation, 58 + cache: Option<InspectionCache>, 56 59 modifiers: SubCommandModifiers, 57 60 ) -> Result<Hive, HiveLibError> { 58 61 info!("evaluating hive {location:?}"); 59 62 63 + if let Some(ref cache) = cache 64 + && let HiveLocation::Flake { prefetch, .. } = location 65 + && let Some(hive) = cache.get_hive(prefetch).await 66 + { 67 + return Ok(hive); 68 + } 69 + 60 70 let output = evaluate_hive_attribute(location, &EvalGoal::Inspect, modifiers).await?; 61 71 62 72 let hive: Hive = serde_json::from_str(&output).map_err(|err| { 63 73 HiveLibError::HiveInitialisationError(HiveInitialisationError::ParseEvaluateError(err)) 64 74 })?; 75 + 76 + if let Some(cache) = cache 77 + && let HiveLocation::Flake { prefetch, .. } = location 78 + { 79 + cache.store_hive(prefetch, &output).await; 80 + } 65 81 66 82 Ok(hive) 67 83 } ··· 169 185 } 170 186 } 171 187 188 + #[derive(Debug, PartialEq, Eq, Deserialize)] 189 + pub struct FlakePrefetch { 190 + pub(crate) hash: String, 191 + #[serde(rename = "storePath")] 192 + pub(crate) store_path: String, 193 + } 194 + 172 195 #[derive(Debug, PartialEq, Eq)] 173 196 pub enum HiveLocation { 174 197 HiveNix(PathBuf), 175 - Flake(String), 198 + Flake { 199 + uri: String, 200 + prefetch: FlakePrefetch, 201 + }, 202 + } 203 + 204 + impl HiveLocation { 205 + async fn get_flake( 206 + uri: String, 207 + modifiers: SubCommandModifiers, 208 + ) -> Result<HiveLocation, HiveLibError> { 209 + let command = run_command( 210 + &CommandArguments::new(format!("nix flake prefetch --extra-experimental-features nix-command --extra-experimental-features flakes --json {uri}"), modifiers) 211 + .mode(crate::commands::ChildOutputMode::Generic), 212 + ) 213 + .await?; 214 + 215 + let result = command 216 + .wait_till_success() 217 + .await 218 + .map_err(HiveLibError::CommandError)?; 219 + 220 + debug!(hash_json = ?result); 221 + 222 + let prefetch = serde_json::from_str(&match result { 223 + Either::Left((.., output)) | Either::Right((.., output)) => output, 224 + }) 225 + .map_err(|x| { 226 + HiveLibError::HiveInitialisationError(HiveInitialisationError::ParsePrefetchError(x)) 227 + })?; 228 + 229 + debug!(prefetch = ?prefetch); 230 + 231 + Ok(HiveLocation::Flake { uri, prefetch }) 232 + } 176 233 } 177 234 178 - pub fn get_hive_location(path: String) -> Result<HiveLocation, HiveLocationError> { 235 + pub async fn get_hive_location( 236 + path: String, 237 + modifiers: SubCommandModifiers, 238 + ) -> Result<HiveLocation, HiveLibError> { 179 239 let flakeref = FlakeRef::from_str(&path); 180 240 181 - let path_to_location = |path: PathBuf| { 241 + let path_to_location = async |path: PathBuf| { 182 242 Ok(match path.file_name().and_then(OsStr::to_str) { 183 243 Some("hive.nix") => HiveLocation::HiveNix(path.clone()), 184 244 Some(_) => { 185 245 if fs::metadata(path.join("flake.nix")).is_ok() { 186 - HiveLocation::Flake(path.display().to_string()) 246 + HiveLocation::get_flake(path.display().to_string(), modifiers).await? 187 247 } else { 188 248 HiveLocation::HiveNix(path.join("hive.nix")) 189 249 } 190 250 } 191 - None => return Err(HiveLocationError::MalformedPath(path.clone())), 251 + None => { 252 + return Err(HiveLibError::HiveLocationError( 253 + HiveLocationError::MalformedPath(path.clone()), 254 + )); 255 + } 192 256 }) 193 257 }; 194 258 195 259 match flakeref { 196 260 Err(nix_compat::flakeref::FlakeRefError::UrlParseError(_err)) => { 197 261 let path = PathBuf::from(path); 198 - Ok(path_to_location(path)?) 262 + Ok(path_to_location(path).await?) 199 263 } 200 - Ok(FlakeRef::Path { path, .. }) => Ok(path_to_location(path)?), 264 + Ok(FlakeRef::Path { path, .. }) => Ok(path_to_location(path).await?), 201 265 Ok( 202 266 FlakeRef::Git { .. } 203 267 | FlakeRef::GitHub { .. } ··· 205 269 | FlakeRef::Tarball { .. } 206 270 | FlakeRef::Mercurial { .. } 207 271 | FlakeRef::SourceHut { .. }, 208 - ) => Ok(HiveLocation::Flake(path)), 209 - Err(err) => Err(HiveLocationError::Malformed(err)), 210 - Ok(flakeref) => Err(HiveLocationError::TypeUnsupported(Box::new(flakeref))), 272 + ) => Ok(HiveLocation::get_flake(path, modifiers).await?), 273 + Err(err) => Err(HiveLibError::HiveLocationError( 274 + HiveLocationError::Malformed(err), 275 + )), 276 + Ok(flakeref) => Err(HiveLibError::HiveLocationError( 277 + HiveLocationError::TypeUnsupported(Box::new(flakeref)), 278 + )), 211 279 } 212 280 } 213 281 ··· 227 295 use std::{assert_matches::assert_matches, env}; 228 296 229 297 // flake should always come before hive.nix 230 - #[test] 231 - fn test_hive_dot_nix_priority() { 298 + #[tokio::test] 299 + async fn test_hive_dot_nix_priority() { 232 300 let location = location!(get_test_path!()); 233 301 234 - assert_matches!(location, HiveLocation::Flake(..)); 302 + assert_matches!(location, HiveLocation::Flake { .. }); 235 303 } 236 304 237 305 #[tokio::test] ··· 239 307 async fn test_hive_file() { 240 308 let location = location!(get_test_path!()); 241 309 242 - let hive = Hive::new_from_path(&location, SubCommandModifiers::default()) 310 + let hive = Hive::new_from_path(&location, None, SubCommandModifiers::default()) 243 311 .await 244 312 .unwrap(); 245 313 ··· 265 333 async fn non_trivial_hive() { 266 334 let location = location!(get_test_path!()); 267 335 268 - let hive = Hive::new_from_path(&location, SubCommandModifiers::default()) 336 + let hive = Hive::new_from_path(&location, None, SubCommandModifiers::default()) 269 337 .await 270 338 .unwrap(); 271 339 ··· 303 371 async fn flake_hive() { 304 372 let tmp_dir = make_flake_sandbox(&get_test_path!()).unwrap(); 305 373 306 - let location = get_hive_location(tmp_dir.path().display().to_string()).unwrap(); 307 - let hive = Hive::new_from_path(&location, SubCommandModifiers::default()) 374 + let location = get_hive_location( 375 + tmp_dir.path().display().to_string(), 376 + SubCommandModifiers::default(), 377 + ) 378 + .await 379 + .unwrap(); 380 + let hive = Hive::new_from_path(&location, None, SubCommandModifiers::default()) 308 381 .await 309 382 .unwrap(); 310 383 ··· 331 404 let location = location!(get_test_path!()); 332 405 333 406 assert_matches!( 334 - Hive::new_from_path(&location, SubCommandModifiers::default()).await, 407 + Hive::new_from_path(&location, None, SubCommandModifiers::default()).await, 335 408 Err(HiveLibError::NixEvalError { 336 409 source: CommandError::CommandFailed { 337 410 logs, ··· 348 421 let location = location!(get_test_path!()); 349 422 350 423 assert_matches!( 351 - Hive::new_from_path(&location, SubCommandModifiers::default()).await, 424 + Hive::new_from_path(&location, None, SubCommandModifiers::default()).await, 352 425 Err(HiveLibError::NixEvalError { 353 426 source: CommandError::CommandFailed { 354 427 logs, ··· 366 439 location.push("non_trivial_hive"); 367 440 let location = location!(location); 368 441 369 - let mut hive = Hive::new_from_path(&location, SubCommandModifiers::default()) 442 + let mut hive = Hive::new_from_path(&location, None, SubCommandModifiers::default()) 370 443 .await 371 444 .unwrap(); 372 445
+5 -2
wire/lib/src/hive/node.rs
··· 489 489 async fn default_values_match() { 490 490 let mut path = get_test_path!(); 491 491 492 - let location = get_hive_location(path.display().to_string()).unwrap(); 493 - let hive = Hive::new_from_path(&location, SubCommandModifiers::default()) 492 + let location = 493 + get_hive_location(path.display().to_string(), SubCommandModifiers::default()) 494 + .await 495 + .unwrap(); 496 + let hive = Hive::new_from_path(&location, None, SubCommandModifiers::default()) 494 497 .await 495 498 .unwrap(); 496 499
+2
wire/lib/src/lib.rs
··· 15 15 16 16 use crate::{errors::HiveLibError, hive::node::Name, status::STATUS}; 17 17 18 + pub mod cache; 18 19 pub mod commands; 19 20 pub mod hive; 20 21 pub mod status; ··· 56 57 57 58 pub enum EvalGoal<'a> { 58 59 Inspect, 60 + Names, 59 61 GetTopLevel(&'a Name), 60 62 } 61 63
+8 -1
wire/lib/src/test_macros.rs
··· 32 32 33 33 #[macro_export] 34 34 macro_rules! location { 35 - ($path:expr) => {{ $crate::hive::get_hive_location($path.display().to_string()).unwrap() }}; 35 + ($path:expr) => {{ 36 + $crate::hive::get_hive_location( 37 + $path.display().to_string(), 38 + $crate::SubCommandModifiers::default(), 39 + ) 40 + .await 41 + .unwrap() 42 + }}; 36 43 }