├── .github ├── dependabot.yml └── workflows │ ├── ci.yaml │ ├── clippy.yaml │ ├── publish.yml │ └── update-flake.yaml ├── .gitignore ├── .rustfmt.toml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── default.nix ├── flake.lock ├── flake.nix ├── nix └── package.nix ├── overlay.nix └── src ├── cli.rs ├── command.rs ├── http.rs ├── main.rs └── nix.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | commit-message: 8 | prefix: "ci" 9 | groups: 10 | actions: 11 | patterns: 12 | - "*" 13 | 14 | - package-ecosystem: "cargo" 15 | directory: "/" 16 | schedule: 17 | interval: "weekly" 18 | commit-message: 19 | prefix: "chore(deps)" 20 | groups: 21 | security: 22 | applies-to: security-updates 23 | patterns: 24 | - "*" 25 | regular: 26 | applies-to: version-updates 27 | update-types: 28 | - minor 29 | - patch 30 | major: 31 | applies-to: version-updates 32 | update-types: 33 | - major 34 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | paths: 7 | - "**.lock" 8 | - "**.rs" 9 | - "**.nix" 10 | - "Cargo.toml" 11 | - ".github/workflows/ci.yaml" 12 | pull_request: 13 | paths: 14 | - "**.lock" 15 | - "**.rs" 16 | - "**.nix" 17 | - "Cargo.toml" 18 | - ".github/workflows/ci.yaml" 19 | workflow_dispatch: 20 | 21 | jobs: 22 | build: 23 | name: Build 24 | 25 | strategy: 26 | fail-fast: false 27 | matrix: 28 | os: [ubuntu-latest, macos-latest] 29 | 30 | runs-on: ${{ matrix.os }} 31 | 32 | steps: 33 | - name: Checkout repository 34 | uses: actions/checkout@v4 35 | 36 | - name: Install Nix 37 | uses: cachix/install-nix-action@v31 38 | 39 | - name: Run build 40 | run: | 41 | nix build --print-build-logs .#nix-forecast-debug 42 | 43 | format-and-lint: 44 | name: Format and lint 45 | 46 | runs-on: ubuntu-latest 47 | 48 | steps: 49 | - name: Checkout repository 50 | uses: actions/checkout@v4 51 | 52 | - name: Install Nix 53 | uses: cachix/install-nix-action@v31 54 | 55 | - name: Run flake checks 56 | run: | 57 | nix flake check --print-build-logs --show-trace 58 | 59 | release-gate: 60 | name: CI Release gate 61 | needs: [build, format-and-lint] 62 | 63 | if: ${{ always() }} 64 | 65 | runs-on: ubuntu-latest 66 | 67 | steps: 68 | - name: Exit with error 69 | if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} 70 | run: exit 1 71 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yaml: -------------------------------------------------------------------------------- 1 | name: Clippy 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | paths: 7 | - "**.lock" 8 | - "**.rs" 9 | - "**.nix" 10 | - "Cargo.toml" 11 | - ".github/workflows/clippy.yaml" 12 | pull_request: 13 | paths: 14 | - "**.lock" 15 | - "**.rs" 16 | - "**.nix" 17 | - "Cargo.toml" 18 | - ".github/workflows/clippy.yaml" 19 | workflow_dispatch: 20 | 21 | jobs: 22 | clippy: 23 | name: Run scan 24 | 25 | runs-on: ubuntu-latest 26 | 27 | permissions: 28 | security-events: write 29 | 30 | steps: 31 | - name: Checkout repository 32 | uses: actions/checkout@v4 33 | 34 | - name: Install Nix 35 | uses: cachix/install-nix-action@v31 36 | 37 | - name: Run clippy 38 | id: clippy-run 39 | run: | 40 | nix build --print-build-logs .#checks.x86_64-linux.clippy 41 | [ -L result ] || exit 1 42 | echo "sarif-file=$(readlink -f result)" >> "$GITHUB_OUTPUT" 43 | 44 | - name: Upload results 45 | uses: github/codeql-action/upload-sarif@v3 46 | with: 47 | sarif_file: ${{ steps.clippy-run.outputs.sarif-file }} 48 | wait-for-processing: true 49 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v?[0-9]+.[0-9]+.[0-9]+*" 7 | workflow_dispatch: 8 | inputs: 9 | tag: 10 | description: "The existing tag to publish" 11 | type: "string" 12 | required: true 13 | 14 | jobs: 15 | flakehub: 16 | name: Publish to FlakeHub 17 | 18 | runs-on: ubuntu-latest 19 | 20 | permissions: 21 | id-token: write 22 | 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@v4 26 | with: 27 | ref: ${{ inputs.tag }} 28 | 29 | - name: Install Nix 30 | uses: cachix/install-nix-action@v31 31 | 32 | - name: Push to FlakeHub 33 | uses: DeterminateSystems/flakehub-push@v5 34 | with: 35 | tag: ${{ inputs.tag }} 36 | visibility: "public" 37 | 38 | flakestry: 39 | name: Publish to Flakestry 40 | 41 | runs-on: ubuntu-latest 42 | 43 | permissions: 44 | id-token: write 45 | 46 | steps: 47 | - name: Checkout repository 48 | uses: actions/checkout@v4 49 | with: 50 | ref: ${{ inputs.tag }} 51 | 52 | - name: Install Nix 53 | uses: cachix/install-nix-action@v31 54 | 55 | - name: Push to Flakestry 56 | # Flakestry sometimes sends bad responses even when 57 | # the flake *has* been published 58 | continue-on-error: true 59 | uses: flakestry/flakestry-publish@main 60 | with: 61 | version: ${{ inputs.tag }} 62 | -------------------------------------------------------------------------------- /.github/workflows/update-flake.yaml: -------------------------------------------------------------------------------- 1 | name: flake.lock 2 | 3 | on: 4 | schedule: 5 | # run every saturday 6 | - cron: "0 0 * * 6" 7 | workflow_dispatch: 8 | 9 | jobs: 10 | update: 11 | name: Update & make PR 12 | 13 | runs-on: ubuntu-latest 14 | 15 | permissions: 16 | contents: write 17 | pull-requests: write 18 | 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v4 22 | 23 | - name: Install Nix 24 | uses: cachix/install-nix-action@v31 25 | 26 | - uses: DeterminateSystems/update-flake-lock@v24 27 | with: 28 | commit-msg: "chore(nix): update flake.lock" 29 | pr-title: "chore(nix): update flake.lock" 30 | token: ${{ github.token }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/8779ee73af62c669e7ca371aaab8399d87127693/Rust.gitignore 2 | 3 | # Generated by Cargo 4 | # will have compiled files and executables 5 | debug/ 6 | target/ 7 | 8 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 9 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 10 | # Cargo.lock 11 | 12 | # These are backup files generated by rustfmt 13 | **/*.rs.bk 14 | 15 | # MSVC Windows builds of rustc generate these, which store debugging information 16 | *.pdb 17 | 18 | # RustRover 19 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 20 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 21 | # and can be added to the global gitignore or merged into this file. For a more nuclear 22 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 23 | #.idea/ 24 | 25 | 26 | ### Nix 27 | 28 | result 29 | result-* 30 | repl-result-* 31 | 32 | 33 | ### https://raw.github.com/github/gitignore/8779ee73af62c669e7ca371aaab8399d87127693/Global/Linux.gitignore 34 | 35 | *~ 36 | 37 | # temporary files which can be created if a process still has a handle open of a deleted file 38 | .fuse_hidden* 39 | 40 | # KDE directory preferences 41 | .directory 42 | 43 | # Linux trash folder which might appear on any partition or disk 44 | .Trash-* 45 | 46 | # .nfs files are created when an open file is removed but is still being accessed 47 | .nfs* 48 | 49 | 50 | ### https://raw.github.com/github/gitignore/8779ee73af62c669e7ca371aaab8399d87127693/Global/macOS.gitignore 51 | 52 | # General 53 | .DS_Store 54 | .AppleDouble 55 | .LSOverride 56 | 57 | # Icon must end with two \r 58 | Icon 59 | 60 | # Thumbnails 61 | ._* 62 | 63 | # Files that might appear in the root of a volume 64 | .DocumentRevisions-V100 65 | .fseventsd 66 | .Spotlight-V100 67 | .TemporaryItems 68 | .Trashes 69 | .VolumeIcon.icns 70 | .com.apple.timemachine.donotpresent 71 | 72 | # Directories potentially created on remote AFP share 73 | .AppleDB 74 | .AppleDesktop 75 | Network Trash Folder 76 | Temporary Items 77 | .apdisk 78 | 79 | 80 | ### https://raw.github.com/github/gitignore/8779ee73af62c669e7ca371aaab8399d87127693/Global/Windows.gitignore 81 | 82 | # Windows thumbnail cache files 83 | Thumbs.db 84 | Thumbs.db:encryptable 85 | ehthumbs.db 86 | ehthumbs_vista.db 87 | 88 | # Dump file 89 | *.stackdump 90 | 91 | # Folder config file 92 | [Dd]esktop.ini 93 | 94 | # Recycle Bin used on file shares 95 | $RECYCLE.BIN/ 96 | 97 | # Windows Installer files 98 | *.cab 99 | *.msi 100 | *.msix 101 | *.msm 102 | *.msp 103 | 104 | # Windows shortcuts 105 | *.lnk 106 | 107 | 108 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "anstream" 22 | version = "0.6.15" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" 25 | dependencies = [ 26 | "anstyle", 27 | "anstyle-parse", 28 | "anstyle-query", 29 | "anstyle-wincon", 30 | "colorchoice", 31 | "is_terminal_polyfill", 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle" 37 | version = "1.0.8" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 40 | 41 | [[package]] 42 | name = "anstyle-parse" 43 | version = "0.2.5" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" 46 | dependencies = [ 47 | "utf8parse", 48 | ] 49 | 50 | [[package]] 51 | name = "anstyle-query" 52 | version = "1.1.1" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" 55 | dependencies = [ 56 | "windows-sys 0.52.0", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle-wincon" 61 | version = "3.0.4" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" 64 | dependencies = [ 65 | "anstyle", 66 | "windows-sys 0.52.0", 67 | ] 68 | 69 | [[package]] 70 | name = "anyhow" 71 | version = "1.0.98" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 74 | 75 | [[package]] 76 | name = "atomic-waker" 77 | version = "1.1.2" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 80 | 81 | [[package]] 82 | name = "autocfg" 83 | version = "1.3.0" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 86 | 87 | [[package]] 88 | name = "backtrace" 89 | version = "0.3.71" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" 92 | dependencies = [ 93 | "addr2line", 94 | "cc", 95 | "cfg-if", 96 | "libc", 97 | "miniz_oxide", 98 | "object", 99 | "rustc-demangle", 100 | ] 101 | 102 | [[package]] 103 | name = "base64" 104 | version = "0.22.1" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 107 | 108 | [[package]] 109 | name = "bitflags" 110 | version = "2.6.0" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 113 | 114 | [[package]] 115 | name = "bumpalo" 116 | version = "3.16.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 119 | 120 | [[package]] 121 | name = "byteorder" 122 | version = "1.5.0" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 125 | 126 | [[package]] 127 | name = "bytes" 128 | version = "1.7.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" 131 | 132 | [[package]] 133 | name = "cc" 134 | version = "1.2.16" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" 137 | dependencies = [ 138 | "shlex", 139 | ] 140 | 141 | [[package]] 142 | name = "cfg-if" 143 | version = "1.0.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 146 | 147 | [[package]] 148 | name = "clap" 149 | version = "4.5.36" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" 152 | dependencies = [ 153 | "clap_builder", 154 | "clap_derive", 155 | ] 156 | 157 | [[package]] 158 | name = "clap_builder" 159 | version = "4.5.36" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" 162 | dependencies = [ 163 | "anstream", 164 | "anstyle", 165 | "clap_lex", 166 | "strsim", 167 | ] 168 | 169 | [[package]] 170 | name = "clap_complete" 171 | version = "4.5.47" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "c06f5378ea264ad4f82bbc826628b5aad714a75abf6ece087e923010eb937fb6" 174 | dependencies = [ 175 | "clap", 176 | ] 177 | 178 | [[package]] 179 | name = "clap_derive" 180 | version = "4.5.32" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" 183 | dependencies = [ 184 | "heck", 185 | "proc-macro2", 186 | "quote", 187 | "syn", 188 | ] 189 | 190 | [[package]] 191 | name = "clap_lex" 192 | version = "0.7.4" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 195 | 196 | [[package]] 197 | name = "colorchoice" 198 | version = "1.0.2" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" 201 | 202 | [[package]] 203 | name = "console" 204 | version = "0.15.8" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" 207 | dependencies = [ 208 | "encode_unicode", 209 | "lazy_static", 210 | "libc", 211 | "unicode-width 0.1.13", 212 | "windows-sys 0.52.0", 213 | ] 214 | 215 | [[package]] 216 | name = "core-foundation" 217 | version = "0.9.4" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 220 | dependencies = [ 221 | "core-foundation-sys", 222 | "libc", 223 | ] 224 | 225 | [[package]] 226 | name = "core-foundation-sys" 227 | version = "0.8.7" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 230 | 231 | [[package]] 232 | name = "encode_unicode" 233 | version = "0.3.6" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 236 | 237 | [[package]] 238 | name = "encoding_rs" 239 | version = "0.8.34" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" 242 | dependencies = [ 243 | "cfg-if", 244 | ] 245 | 246 | [[package]] 247 | name = "equivalent" 248 | version = "1.0.1" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 251 | 252 | [[package]] 253 | name = "fnv" 254 | version = "1.0.7" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 257 | 258 | [[package]] 259 | name = "form_urlencoded" 260 | version = "1.2.1" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 263 | dependencies = [ 264 | "percent-encoding", 265 | ] 266 | 267 | [[package]] 268 | name = "futures" 269 | version = "0.3.31" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 272 | dependencies = [ 273 | "futures-channel", 274 | "futures-core", 275 | "futures-executor", 276 | "futures-io", 277 | "futures-sink", 278 | "futures-task", 279 | "futures-util", 280 | ] 281 | 282 | [[package]] 283 | name = "futures-channel" 284 | version = "0.3.31" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 287 | dependencies = [ 288 | "futures-core", 289 | "futures-sink", 290 | ] 291 | 292 | [[package]] 293 | name = "futures-core" 294 | version = "0.3.31" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 297 | 298 | [[package]] 299 | name = "futures-executor" 300 | version = "0.3.31" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 303 | dependencies = [ 304 | "futures-core", 305 | "futures-task", 306 | "futures-util", 307 | ] 308 | 309 | [[package]] 310 | name = "futures-io" 311 | version = "0.3.31" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 314 | 315 | [[package]] 316 | name = "futures-macro" 317 | version = "0.3.31" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 320 | dependencies = [ 321 | "proc-macro2", 322 | "quote", 323 | "syn", 324 | ] 325 | 326 | [[package]] 327 | name = "futures-sink" 328 | version = "0.3.31" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 331 | 332 | [[package]] 333 | name = "futures-task" 334 | version = "0.3.31" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 337 | 338 | [[package]] 339 | name = "futures-util" 340 | version = "0.3.31" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 343 | dependencies = [ 344 | "futures-channel", 345 | "futures-core", 346 | "futures-io", 347 | "futures-macro", 348 | "futures-sink", 349 | "futures-task", 350 | "memchr", 351 | "pin-project-lite", 352 | "pin-utils", 353 | "slab", 354 | ] 355 | 356 | [[package]] 357 | name = "getrandom" 358 | version = "0.2.15" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 361 | dependencies = [ 362 | "cfg-if", 363 | "libc", 364 | "wasi", 365 | ] 366 | 367 | [[package]] 368 | name = "gimli" 369 | version = "0.28.1" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 372 | 373 | [[package]] 374 | name = "h2" 375 | version = "0.4.6" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" 378 | dependencies = [ 379 | "atomic-waker", 380 | "bytes", 381 | "fnv", 382 | "futures-core", 383 | "futures-sink", 384 | "http", 385 | "indexmap", 386 | "slab", 387 | "tokio", 388 | "tokio-util", 389 | "tracing", 390 | ] 391 | 392 | [[package]] 393 | name = "hashbrown" 394 | version = "0.14.5" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 397 | 398 | [[package]] 399 | name = "heck" 400 | version = "0.5.0" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 403 | 404 | [[package]] 405 | name = "hermit-abi" 406 | version = "0.3.9" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 409 | 410 | [[package]] 411 | name = "http" 412 | version = "1.1.0" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" 415 | dependencies = [ 416 | "bytes", 417 | "fnv", 418 | "itoa", 419 | ] 420 | 421 | [[package]] 422 | name = "http-body" 423 | version = "1.0.1" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 426 | dependencies = [ 427 | "bytes", 428 | "http", 429 | ] 430 | 431 | [[package]] 432 | name = "http-body-util" 433 | version = "0.1.2" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" 436 | dependencies = [ 437 | "bytes", 438 | "futures-util", 439 | "http", 440 | "http-body", 441 | "pin-project-lite", 442 | ] 443 | 444 | [[package]] 445 | name = "httparse" 446 | version = "1.9.4" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" 449 | 450 | [[package]] 451 | name = "hyper" 452 | version = "1.4.1" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" 455 | dependencies = [ 456 | "bytes", 457 | "futures-channel", 458 | "futures-util", 459 | "h2", 460 | "http", 461 | "http-body", 462 | "httparse", 463 | "itoa", 464 | "pin-project-lite", 465 | "smallvec", 466 | "tokio", 467 | "want", 468 | ] 469 | 470 | [[package]] 471 | name = "hyper-rustls" 472 | version = "0.27.3" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" 475 | dependencies = [ 476 | "futures-util", 477 | "http", 478 | "hyper", 479 | "hyper-util", 480 | "rustls", 481 | "rustls-pki-types", 482 | "tokio", 483 | "tokio-rustls", 484 | "tower-service", 485 | "webpki-roots", 486 | ] 487 | 488 | [[package]] 489 | name = "hyper-util" 490 | version = "0.1.10" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" 493 | dependencies = [ 494 | "bytes", 495 | "futures-channel", 496 | "futures-util", 497 | "http", 498 | "http-body", 499 | "hyper", 500 | "pin-project-lite", 501 | "socket2", 502 | "tokio", 503 | "tower-service", 504 | "tracing", 505 | ] 506 | 507 | [[package]] 508 | name = "idna" 509 | version = "0.5.0" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 512 | dependencies = [ 513 | "unicode-bidi", 514 | "unicode-normalization", 515 | ] 516 | 517 | [[package]] 518 | name = "indexmap" 519 | version = "2.5.0" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" 522 | dependencies = [ 523 | "equivalent", 524 | "hashbrown", 525 | ] 526 | 527 | [[package]] 528 | name = "indicatif" 529 | version = "0.17.11" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" 532 | dependencies = [ 533 | "console", 534 | "number_prefix", 535 | "portable-atomic", 536 | "unicode-width 0.2.0", 537 | "web-time", 538 | ] 539 | 540 | [[package]] 541 | name = "ipnet" 542 | version = "2.9.0" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" 545 | 546 | [[package]] 547 | name = "is_terminal_polyfill" 548 | version = "1.70.1" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 551 | 552 | [[package]] 553 | name = "itoa" 554 | version = "1.0.11" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 557 | 558 | [[package]] 559 | name = "js-sys" 560 | version = "0.3.77" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 563 | dependencies = [ 564 | "once_cell", 565 | "wasm-bindgen", 566 | ] 567 | 568 | [[package]] 569 | name = "lazy_static" 570 | version = "1.5.0" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 573 | 574 | [[package]] 575 | name = "libc" 576 | version = "0.2.171" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 579 | 580 | [[package]] 581 | name = "log" 582 | version = "0.4.22" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 585 | 586 | [[package]] 587 | name = "memchr" 588 | version = "2.7.4" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 591 | 592 | [[package]] 593 | name = "mime" 594 | version = "0.3.17" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 597 | 598 | [[package]] 599 | name = "miniz_oxide" 600 | version = "0.7.4" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" 603 | dependencies = [ 604 | "adler", 605 | ] 606 | 607 | [[package]] 608 | name = "mio" 609 | version = "1.0.2" 610 | source = "registry+https://github.com/rust-lang/crates.io-index" 611 | checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" 612 | dependencies = [ 613 | "hermit-abi", 614 | "libc", 615 | "wasi", 616 | "windows-sys 0.52.0", 617 | ] 618 | 619 | [[package]] 620 | name = "nix-forecast" 621 | version = "0.2.0" 622 | dependencies = [ 623 | "anyhow", 624 | "clap", 625 | "clap_complete", 626 | "futures", 627 | "indicatif", 628 | "num_cpus", 629 | "reqwest", 630 | "serde", 631 | "serde_json", 632 | "thiserror 2.0.12", 633 | "tokio", 634 | "tracing", 635 | "tracing-subscriber", 636 | ] 637 | 638 | [[package]] 639 | name = "nu-ansi-term" 640 | version = "0.46.0" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 643 | dependencies = [ 644 | "overload", 645 | "winapi", 646 | ] 647 | 648 | [[package]] 649 | name = "num_cpus" 650 | version = "1.16.0" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 653 | dependencies = [ 654 | "hermit-abi", 655 | "libc", 656 | ] 657 | 658 | [[package]] 659 | name = "number_prefix" 660 | version = "0.4.0" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" 663 | 664 | [[package]] 665 | name = "object" 666 | version = "0.32.2" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 669 | dependencies = [ 670 | "memchr", 671 | ] 672 | 673 | [[package]] 674 | name = "once_cell" 675 | version = "1.19.0" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 678 | 679 | [[package]] 680 | name = "overload" 681 | version = "0.1.1" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 684 | 685 | [[package]] 686 | name = "percent-encoding" 687 | version = "2.3.1" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 690 | 691 | [[package]] 692 | name = "pin-project-lite" 693 | version = "0.2.14" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 696 | 697 | [[package]] 698 | name = "pin-utils" 699 | version = "0.1.0" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 702 | 703 | [[package]] 704 | name = "portable-atomic" 705 | version = "1.7.0" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" 708 | 709 | [[package]] 710 | name = "ppv-lite86" 711 | version = "0.2.20" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 714 | dependencies = [ 715 | "zerocopy", 716 | ] 717 | 718 | [[package]] 719 | name = "proc-macro2" 720 | version = "1.0.86" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 723 | dependencies = [ 724 | "unicode-ident", 725 | ] 726 | 727 | [[package]] 728 | name = "quinn" 729 | version = "0.11.5" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" 732 | dependencies = [ 733 | "bytes", 734 | "pin-project-lite", 735 | "quinn-proto", 736 | "quinn-udp", 737 | "rustc-hash", 738 | "rustls", 739 | "socket2", 740 | "thiserror 1.0.69", 741 | "tokio", 742 | "tracing", 743 | ] 744 | 745 | [[package]] 746 | name = "quinn-proto" 747 | version = "0.11.8" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" 750 | dependencies = [ 751 | "bytes", 752 | "rand", 753 | "ring", 754 | "rustc-hash", 755 | "rustls", 756 | "slab", 757 | "thiserror 1.0.69", 758 | "tinyvec", 759 | "tracing", 760 | ] 761 | 762 | [[package]] 763 | name = "quinn-udp" 764 | version = "0.5.5" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" 767 | dependencies = [ 768 | "libc", 769 | "once_cell", 770 | "socket2", 771 | "tracing", 772 | "windows-sys 0.59.0", 773 | ] 774 | 775 | [[package]] 776 | name = "quote" 777 | version = "1.0.37" 778 | source = "registry+https://github.com/rust-lang/crates.io-index" 779 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 780 | dependencies = [ 781 | "proc-macro2", 782 | ] 783 | 784 | [[package]] 785 | name = "rand" 786 | version = "0.8.5" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 789 | dependencies = [ 790 | "libc", 791 | "rand_chacha", 792 | "rand_core", 793 | ] 794 | 795 | [[package]] 796 | name = "rand_chacha" 797 | version = "0.3.1" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 800 | dependencies = [ 801 | "ppv-lite86", 802 | "rand_core", 803 | ] 804 | 805 | [[package]] 806 | name = "rand_core" 807 | version = "0.6.4" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 810 | dependencies = [ 811 | "getrandom", 812 | ] 813 | 814 | [[package]] 815 | name = "reqwest" 816 | version = "0.12.15" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" 819 | dependencies = [ 820 | "base64", 821 | "bytes", 822 | "encoding_rs", 823 | "futures-core", 824 | "futures-util", 825 | "h2", 826 | "http", 827 | "http-body", 828 | "http-body-util", 829 | "hyper", 830 | "hyper-rustls", 831 | "hyper-util", 832 | "ipnet", 833 | "js-sys", 834 | "log", 835 | "mime", 836 | "once_cell", 837 | "percent-encoding", 838 | "pin-project-lite", 839 | "quinn", 840 | "rustls", 841 | "rustls-pemfile", 842 | "rustls-pki-types", 843 | "serde", 844 | "serde_json", 845 | "serde_urlencoded", 846 | "sync_wrapper", 847 | "system-configuration", 848 | "tokio", 849 | "tokio-rustls", 850 | "tower", 851 | "tower-service", 852 | "url", 853 | "wasm-bindgen", 854 | "wasm-bindgen-futures", 855 | "web-sys", 856 | "webpki-roots", 857 | "windows-registry", 858 | ] 859 | 860 | [[package]] 861 | name = "ring" 862 | version = "0.17.13" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" 865 | dependencies = [ 866 | "cc", 867 | "cfg-if", 868 | "getrandom", 869 | "libc", 870 | "untrusted", 871 | "windows-sys 0.52.0", 872 | ] 873 | 874 | [[package]] 875 | name = "rustc-demangle" 876 | version = "0.1.24" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 879 | 880 | [[package]] 881 | name = "rustc-hash" 882 | version = "2.0.0" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" 885 | 886 | [[package]] 887 | name = "rustls" 888 | version = "0.23.12" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" 891 | dependencies = [ 892 | "once_cell", 893 | "ring", 894 | "rustls-pki-types", 895 | "rustls-webpki", 896 | "subtle", 897 | "zeroize", 898 | ] 899 | 900 | [[package]] 901 | name = "rustls-pemfile" 902 | version = "2.1.3" 903 | source = "registry+https://github.com/rust-lang/crates.io-index" 904 | checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" 905 | dependencies = [ 906 | "base64", 907 | "rustls-pki-types", 908 | ] 909 | 910 | [[package]] 911 | name = "rustls-pki-types" 912 | version = "1.8.0" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" 915 | 916 | [[package]] 917 | name = "rustls-webpki" 918 | version = "0.102.7" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" 921 | dependencies = [ 922 | "ring", 923 | "rustls-pki-types", 924 | "untrusted", 925 | ] 926 | 927 | [[package]] 928 | name = "rustversion" 929 | version = "1.0.20" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 932 | 933 | [[package]] 934 | name = "ryu" 935 | version = "1.0.18" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 938 | 939 | [[package]] 940 | name = "serde" 941 | version = "1.0.219" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 944 | dependencies = [ 945 | "serde_derive", 946 | ] 947 | 948 | [[package]] 949 | name = "serde_derive" 950 | version = "1.0.219" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 953 | dependencies = [ 954 | "proc-macro2", 955 | "quote", 956 | "syn", 957 | ] 958 | 959 | [[package]] 960 | name = "serde_json" 961 | version = "1.0.140" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 964 | dependencies = [ 965 | "itoa", 966 | "memchr", 967 | "ryu", 968 | "serde", 969 | ] 970 | 971 | [[package]] 972 | name = "serde_urlencoded" 973 | version = "0.7.1" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 976 | dependencies = [ 977 | "form_urlencoded", 978 | "itoa", 979 | "ryu", 980 | "serde", 981 | ] 982 | 983 | [[package]] 984 | name = "sharded-slab" 985 | version = "0.1.7" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 988 | dependencies = [ 989 | "lazy_static", 990 | ] 991 | 992 | [[package]] 993 | name = "shlex" 994 | version = "1.3.0" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 997 | 998 | [[package]] 999 | name = "slab" 1000 | version = "0.4.9" 1001 | source = "registry+https://github.com/rust-lang/crates.io-index" 1002 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1003 | dependencies = [ 1004 | "autocfg", 1005 | ] 1006 | 1007 | [[package]] 1008 | name = "smallvec" 1009 | version = "1.13.2" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1012 | 1013 | [[package]] 1014 | name = "socket2" 1015 | version = "0.5.7" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 1018 | dependencies = [ 1019 | "libc", 1020 | "windows-sys 0.52.0", 1021 | ] 1022 | 1023 | [[package]] 1024 | name = "strsim" 1025 | version = "0.11.1" 1026 | source = "registry+https://github.com/rust-lang/crates.io-index" 1027 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1028 | 1029 | [[package]] 1030 | name = "subtle" 1031 | version = "2.6.1" 1032 | source = "registry+https://github.com/rust-lang/crates.io-index" 1033 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1034 | 1035 | [[package]] 1036 | name = "syn" 1037 | version = "2.0.87" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" 1040 | dependencies = [ 1041 | "proc-macro2", 1042 | "quote", 1043 | "unicode-ident", 1044 | ] 1045 | 1046 | [[package]] 1047 | name = "sync_wrapper" 1048 | version = "1.0.1" 1049 | source = "registry+https://github.com/rust-lang/crates.io-index" 1050 | checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" 1051 | dependencies = [ 1052 | "futures-core", 1053 | ] 1054 | 1055 | [[package]] 1056 | name = "system-configuration" 1057 | version = "0.6.1" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" 1060 | dependencies = [ 1061 | "bitflags", 1062 | "core-foundation", 1063 | "system-configuration-sys", 1064 | ] 1065 | 1066 | [[package]] 1067 | name = "system-configuration-sys" 1068 | version = "0.6.0" 1069 | source = "registry+https://github.com/rust-lang/crates.io-index" 1070 | checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 1071 | dependencies = [ 1072 | "core-foundation-sys", 1073 | "libc", 1074 | ] 1075 | 1076 | [[package]] 1077 | name = "thiserror" 1078 | version = "1.0.69" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1081 | dependencies = [ 1082 | "thiserror-impl 1.0.69", 1083 | ] 1084 | 1085 | [[package]] 1086 | name = "thiserror" 1087 | version = "2.0.12" 1088 | source = "registry+https://github.com/rust-lang/crates.io-index" 1089 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 1090 | dependencies = [ 1091 | "thiserror-impl 2.0.12", 1092 | ] 1093 | 1094 | [[package]] 1095 | name = "thiserror-impl" 1096 | version = "1.0.69" 1097 | source = "registry+https://github.com/rust-lang/crates.io-index" 1098 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1099 | dependencies = [ 1100 | "proc-macro2", 1101 | "quote", 1102 | "syn", 1103 | ] 1104 | 1105 | [[package]] 1106 | name = "thiserror-impl" 1107 | version = "2.0.12" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 1110 | dependencies = [ 1111 | "proc-macro2", 1112 | "quote", 1113 | "syn", 1114 | ] 1115 | 1116 | [[package]] 1117 | name = "thread_local" 1118 | version = "1.1.8" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 1121 | dependencies = [ 1122 | "cfg-if", 1123 | "once_cell", 1124 | ] 1125 | 1126 | [[package]] 1127 | name = "tinyvec" 1128 | version = "1.8.0" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" 1131 | dependencies = [ 1132 | "tinyvec_macros", 1133 | ] 1134 | 1135 | [[package]] 1136 | name = "tinyvec_macros" 1137 | version = "0.1.1" 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" 1139 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1140 | 1141 | [[package]] 1142 | name = "tokio" 1143 | version = "1.44.2" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" 1146 | dependencies = [ 1147 | "backtrace", 1148 | "bytes", 1149 | "libc", 1150 | "mio", 1151 | "pin-project-lite", 1152 | "socket2", 1153 | "tokio-macros", 1154 | "windows-sys 0.52.0", 1155 | ] 1156 | 1157 | [[package]] 1158 | name = "tokio-macros" 1159 | version = "2.5.0" 1160 | source = "registry+https://github.com/rust-lang/crates.io-index" 1161 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1162 | dependencies = [ 1163 | "proc-macro2", 1164 | "quote", 1165 | "syn", 1166 | ] 1167 | 1168 | [[package]] 1169 | name = "tokio-rustls" 1170 | version = "0.26.0" 1171 | source = "registry+https://github.com/rust-lang/crates.io-index" 1172 | checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" 1173 | dependencies = [ 1174 | "rustls", 1175 | "rustls-pki-types", 1176 | "tokio", 1177 | ] 1178 | 1179 | [[package]] 1180 | name = "tokio-util" 1181 | version = "0.7.12" 1182 | source = "registry+https://github.com/rust-lang/crates.io-index" 1183 | checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" 1184 | dependencies = [ 1185 | "bytes", 1186 | "futures-core", 1187 | "futures-sink", 1188 | "pin-project-lite", 1189 | "tokio", 1190 | ] 1191 | 1192 | [[package]] 1193 | name = "tower" 1194 | version = "0.5.2" 1195 | source = "registry+https://github.com/rust-lang/crates.io-index" 1196 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 1197 | dependencies = [ 1198 | "futures-core", 1199 | "futures-util", 1200 | "pin-project-lite", 1201 | "sync_wrapper", 1202 | "tokio", 1203 | "tower-layer", 1204 | "tower-service", 1205 | ] 1206 | 1207 | [[package]] 1208 | name = "tower-layer" 1209 | version = "0.3.3" 1210 | source = "registry+https://github.com/rust-lang/crates.io-index" 1211 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1212 | 1213 | [[package]] 1214 | name = "tower-service" 1215 | version = "0.3.3" 1216 | source = "registry+https://github.com/rust-lang/crates.io-index" 1217 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1218 | 1219 | [[package]] 1220 | name = "tracing" 1221 | version = "0.1.41" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1224 | dependencies = [ 1225 | "pin-project-lite", 1226 | "tracing-attributes", 1227 | "tracing-core", 1228 | ] 1229 | 1230 | [[package]] 1231 | name = "tracing-attributes" 1232 | version = "0.1.28" 1233 | source = "registry+https://github.com/rust-lang/crates.io-index" 1234 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 1235 | dependencies = [ 1236 | "proc-macro2", 1237 | "quote", 1238 | "syn", 1239 | ] 1240 | 1241 | [[package]] 1242 | name = "tracing-core" 1243 | version = "0.1.33" 1244 | source = "registry+https://github.com/rust-lang/crates.io-index" 1245 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 1246 | dependencies = [ 1247 | "once_cell", 1248 | "valuable", 1249 | ] 1250 | 1251 | [[package]] 1252 | name = "tracing-log" 1253 | version = "0.2.0" 1254 | source = "registry+https://github.com/rust-lang/crates.io-index" 1255 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 1256 | dependencies = [ 1257 | "log", 1258 | "once_cell", 1259 | "tracing-core", 1260 | ] 1261 | 1262 | [[package]] 1263 | name = "tracing-subscriber" 1264 | version = "0.3.19" 1265 | source = "registry+https://github.com/rust-lang/crates.io-index" 1266 | checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 1267 | dependencies = [ 1268 | "nu-ansi-term", 1269 | "sharded-slab", 1270 | "smallvec", 1271 | "thread_local", 1272 | "tracing-core", 1273 | "tracing-log", 1274 | ] 1275 | 1276 | [[package]] 1277 | name = "try-lock" 1278 | version = "0.2.5" 1279 | source = "registry+https://github.com/rust-lang/crates.io-index" 1280 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1281 | 1282 | [[package]] 1283 | name = "unicode-bidi" 1284 | version = "0.3.15" 1285 | source = "registry+https://github.com/rust-lang/crates.io-index" 1286 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 1287 | 1288 | [[package]] 1289 | name = "unicode-ident" 1290 | version = "1.0.12" 1291 | source = "registry+https://github.com/rust-lang/crates.io-index" 1292 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1293 | 1294 | [[package]] 1295 | name = "unicode-normalization" 1296 | version = "0.1.23" 1297 | source = "registry+https://github.com/rust-lang/crates.io-index" 1298 | checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 1299 | dependencies = [ 1300 | "tinyvec", 1301 | ] 1302 | 1303 | [[package]] 1304 | name = "unicode-width" 1305 | version = "0.1.13" 1306 | source = "registry+https://github.com/rust-lang/crates.io-index" 1307 | checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" 1308 | 1309 | [[package]] 1310 | name = "unicode-width" 1311 | version = "0.2.0" 1312 | source = "registry+https://github.com/rust-lang/crates.io-index" 1313 | checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" 1314 | 1315 | [[package]] 1316 | name = "untrusted" 1317 | version = "0.9.0" 1318 | source = "registry+https://github.com/rust-lang/crates.io-index" 1319 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1320 | 1321 | [[package]] 1322 | name = "url" 1323 | version = "2.5.2" 1324 | source = "registry+https://github.com/rust-lang/crates.io-index" 1325 | checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" 1326 | dependencies = [ 1327 | "form_urlencoded", 1328 | "idna", 1329 | "percent-encoding", 1330 | ] 1331 | 1332 | [[package]] 1333 | name = "utf8parse" 1334 | version = "0.2.2" 1335 | source = "registry+https://github.com/rust-lang/crates.io-index" 1336 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1337 | 1338 | [[package]] 1339 | name = "valuable" 1340 | version = "0.1.0" 1341 | source = "registry+https://github.com/rust-lang/crates.io-index" 1342 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 1343 | 1344 | [[package]] 1345 | name = "want" 1346 | version = "0.3.1" 1347 | source = "registry+https://github.com/rust-lang/crates.io-index" 1348 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1349 | dependencies = [ 1350 | "try-lock", 1351 | ] 1352 | 1353 | [[package]] 1354 | name = "wasi" 1355 | version = "0.11.0+wasi-snapshot-preview1" 1356 | source = "registry+https://github.com/rust-lang/crates.io-index" 1357 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1358 | 1359 | [[package]] 1360 | name = "wasm-bindgen" 1361 | version = "0.2.100" 1362 | source = "registry+https://github.com/rust-lang/crates.io-index" 1363 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1364 | dependencies = [ 1365 | "cfg-if", 1366 | "once_cell", 1367 | "rustversion", 1368 | "wasm-bindgen-macro", 1369 | ] 1370 | 1371 | [[package]] 1372 | name = "wasm-bindgen-backend" 1373 | version = "0.2.100" 1374 | source = "registry+https://github.com/rust-lang/crates.io-index" 1375 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1376 | dependencies = [ 1377 | "bumpalo", 1378 | "log", 1379 | "proc-macro2", 1380 | "quote", 1381 | "syn", 1382 | "wasm-bindgen-shared", 1383 | ] 1384 | 1385 | [[package]] 1386 | name = "wasm-bindgen-futures" 1387 | version = "0.4.43" 1388 | source = "registry+https://github.com/rust-lang/crates.io-index" 1389 | checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" 1390 | dependencies = [ 1391 | "cfg-if", 1392 | "js-sys", 1393 | "wasm-bindgen", 1394 | "web-sys", 1395 | ] 1396 | 1397 | [[package]] 1398 | name = "wasm-bindgen-macro" 1399 | version = "0.2.100" 1400 | source = "registry+https://github.com/rust-lang/crates.io-index" 1401 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 1402 | dependencies = [ 1403 | "quote", 1404 | "wasm-bindgen-macro-support", 1405 | ] 1406 | 1407 | [[package]] 1408 | name = "wasm-bindgen-macro-support" 1409 | version = "0.2.100" 1410 | source = "registry+https://github.com/rust-lang/crates.io-index" 1411 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 1412 | dependencies = [ 1413 | "proc-macro2", 1414 | "quote", 1415 | "syn", 1416 | "wasm-bindgen-backend", 1417 | "wasm-bindgen-shared", 1418 | ] 1419 | 1420 | [[package]] 1421 | name = "wasm-bindgen-shared" 1422 | version = "0.2.100" 1423 | source = "registry+https://github.com/rust-lang/crates.io-index" 1424 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 1425 | dependencies = [ 1426 | "unicode-ident", 1427 | ] 1428 | 1429 | [[package]] 1430 | name = "web-sys" 1431 | version = "0.3.70" 1432 | source = "registry+https://github.com/rust-lang/crates.io-index" 1433 | checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" 1434 | dependencies = [ 1435 | "js-sys", 1436 | "wasm-bindgen", 1437 | ] 1438 | 1439 | [[package]] 1440 | name = "web-time" 1441 | version = "1.1.0" 1442 | source = "registry+https://github.com/rust-lang/crates.io-index" 1443 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 1444 | dependencies = [ 1445 | "js-sys", 1446 | "wasm-bindgen", 1447 | ] 1448 | 1449 | [[package]] 1450 | name = "webpki-roots" 1451 | version = "0.26.5" 1452 | source = "registry+https://github.com/rust-lang/crates.io-index" 1453 | checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" 1454 | dependencies = [ 1455 | "rustls-pki-types", 1456 | ] 1457 | 1458 | [[package]] 1459 | name = "winapi" 1460 | version = "0.3.9" 1461 | source = "registry+https://github.com/rust-lang/crates.io-index" 1462 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1463 | dependencies = [ 1464 | "winapi-i686-pc-windows-gnu", 1465 | "winapi-x86_64-pc-windows-gnu", 1466 | ] 1467 | 1468 | [[package]] 1469 | name = "winapi-i686-pc-windows-gnu" 1470 | version = "0.4.0" 1471 | source = "registry+https://github.com/rust-lang/crates.io-index" 1472 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1473 | 1474 | [[package]] 1475 | name = "winapi-x86_64-pc-windows-gnu" 1476 | version = "0.4.0" 1477 | source = "registry+https://github.com/rust-lang/crates.io-index" 1478 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1479 | 1480 | [[package]] 1481 | name = "windows-link" 1482 | version = "0.1.0" 1483 | source = "registry+https://github.com/rust-lang/crates.io-index" 1484 | checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" 1485 | 1486 | [[package]] 1487 | name = "windows-registry" 1488 | version = "0.4.0" 1489 | source = "registry+https://github.com/rust-lang/crates.io-index" 1490 | checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" 1491 | dependencies = [ 1492 | "windows-result", 1493 | "windows-strings", 1494 | "windows-targets 0.53.0", 1495 | ] 1496 | 1497 | [[package]] 1498 | name = "windows-result" 1499 | version = "0.3.1" 1500 | source = "registry+https://github.com/rust-lang/crates.io-index" 1501 | checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189" 1502 | dependencies = [ 1503 | "windows-link", 1504 | ] 1505 | 1506 | [[package]] 1507 | name = "windows-strings" 1508 | version = "0.3.1" 1509 | source = "registry+https://github.com/rust-lang/crates.io-index" 1510 | checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" 1511 | dependencies = [ 1512 | "windows-link", 1513 | ] 1514 | 1515 | [[package]] 1516 | name = "windows-sys" 1517 | version = "0.52.0" 1518 | source = "registry+https://github.com/rust-lang/crates.io-index" 1519 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1520 | dependencies = [ 1521 | "windows-targets 0.52.6", 1522 | ] 1523 | 1524 | [[package]] 1525 | name = "windows-sys" 1526 | version = "0.59.0" 1527 | source = "registry+https://github.com/rust-lang/crates.io-index" 1528 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1529 | dependencies = [ 1530 | "windows-targets 0.52.6", 1531 | ] 1532 | 1533 | [[package]] 1534 | name = "windows-targets" 1535 | version = "0.52.6" 1536 | source = "registry+https://github.com/rust-lang/crates.io-index" 1537 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1538 | dependencies = [ 1539 | "windows_aarch64_gnullvm 0.52.6", 1540 | "windows_aarch64_msvc 0.52.6", 1541 | "windows_i686_gnu 0.52.6", 1542 | "windows_i686_gnullvm 0.52.6", 1543 | "windows_i686_msvc 0.52.6", 1544 | "windows_x86_64_gnu 0.52.6", 1545 | "windows_x86_64_gnullvm 0.52.6", 1546 | "windows_x86_64_msvc 0.52.6", 1547 | ] 1548 | 1549 | [[package]] 1550 | name = "windows-targets" 1551 | version = "0.53.0" 1552 | source = "registry+https://github.com/rust-lang/crates.io-index" 1553 | checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" 1554 | dependencies = [ 1555 | "windows_aarch64_gnullvm 0.53.0", 1556 | "windows_aarch64_msvc 0.53.0", 1557 | "windows_i686_gnu 0.53.0", 1558 | "windows_i686_gnullvm 0.53.0", 1559 | "windows_i686_msvc 0.53.0", 1560 | "windows_x86_64_gnu 0.53.0", 1561 | "windows_x86_64_gnullvm 0.53.0", 1562 | "windows_x86_64_msvc 0.53.0", 1563 | ] 1564 | 1565 | [[package]] 1566 | name = "windows_aarch64_gnullvm" 1567 | version = "0.52.6" 1568 | source = "registry+https://github.com/rust-lang/crates.io-index" 1569 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1570 | 1571 | [[package]] 1572 | name = "windows_aarch64_gnullvm" 1573 | version = "0.53.0" 1574 | source = "registry+https://github.com/rust-lang/crates.io-index" 1575 | checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 1576 | 1577 | [[package]] 1578 | name = "windows_aarch64_msvc" 1579 | version = "0.52.6" 1580 | source = "registry+https://github.com/rust-lang/crates.io-index" 1581 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1582 | 1583 | [[package]] 1584 | name = "windows_aarch64_msvc" 1585 | version = "0.53.0" 1586 | source = "registry+https://github.com/rust-lang/crates.io-index" 1587 | checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 1588 | 1589 | [[package]] 1590 | name = "windows_i686_gnu" 1591 | version = "0.52.6" 1592 | source = "registry+https://github.com/rust-lang/crates.io-index" 1593 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1594 | 1595 | [[package]] 1596 | name = "windows_i686_gnu" 1597 | version = "0.53.0" 1598 | source = "registry+https://github.com/rust-lang/crates.io-index" 1599 | checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 1600 | 1601 | [[package]] 1602 | name = "windows_i686_gnullvm" 1603 | version = "0.52.6" 1604 | source = "registry+https://github.com/rust-lang/crates.io-index" 1605 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1606 | 1607 | [[package]] 1608 | name = "windows_i686_gnullvm" 1609 | version = "0.53.0" 1610 | source = "registry+https://github.com/rust-lang/crates.io-index" 1611 | checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 1612 | 1613 | [[package]] 1614 | name = "windows_i686_msvc" 1615 | version = "0.52.6" 1616 | source = "registry+https://github.com/rust-lang/crates.io-index" 1617 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1618 | 1619 | [[package]] 1620 | name = "windows_i686_msvc" 1621 | version = "0.53.0" 1622 | source = "registry+https://github.com/rust-lang/crates.io-index" 1623 | checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 1624 | 1625 | [[package]] 1626 | name = "windows_x86_64_gnu" 1627 | version = "0.52.6" 1628 | source = "registry+https://github.com/rust-lang/crates.io-index" 1629 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1630 | 1631 | [[package]] 1632 | name = "windows_x86_64_gnu" 1633 | version = "0.53.0" 1634 | source = "registry+https://github.com/rust-lang/crates.io-index" 1635 | checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 1636 | 1637 | [[package]] 1638 | name = "windows_x86_64_gnullvm" 1639 | version = "0.52.6" 1640 | source = "registry+https://github.com/rust-lang/crates.io-index" 1641 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1642 | 1643 | [[package]] 1644 | name = "windows_x86_64_gnullvm" 1645 | version = "0.53.0" 1646 | source = "registry+https://github.com/rust-lang/crates.io-index" 1647 | checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 1648 | 1649 | [[package]] 1650 | name = "windows_x86_64_msvc" 1651 | version = "0.52.6" 1652 | source = "registry+https://github.com/rust-lang/crates.io-index" 1653 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1654 | 1655 | [[package]] 1656 | name = "windows_x86_64_msvc" 1657 | version = "0.53.0" 1658 | source = "registry+https://github.com/rust-lang/crates.io-index" 1659 | checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 1660 | 1661 | [[package]] 1662 | name = "zerocopy" 1663 | version = "0.7.35" 1664 | source = "registry+https://github.com/rust-lang/crates.io-index" 1665 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 1666 | dependencies = [ 1667 | "byteorder", 1668 | "zerocopy-derive", 1669 | ] 1670 | 1671 | [[package]] 1672 | name = "zerocopy-derive" 1673 | version = "0.7.35" 1674 | source = "registry+https://github.com/rust-lang/crates.io-index" 1675 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 1676 | dependencies = [ 1677 | "proc-macro2", 1678 | "quote", 1679 | "syn", 1680 | ] 1681 | 1682 | [[package]] 1683 | name = "zeroize" 1684 | version = "1.8.1" 1685 | source = "registry+https://github.com/rust-lang/crates.io-index" 1686 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 1687 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nix-forecast" 3 | version = "0.2.0" 4 | authors = [ "Seth ", "nix-forecast Contributors" ] 5 | edition = "2021" 6 | description = "Check the forecast for today's Nix builds" 7 | readme = "README.md" 8 | repository = "https://github.com/getchoo/nix-forecast" 9 | license = "MPL-2.0" 10 | keywords = ["nix"] 11 | categories = ["command-line-utilities"] 12 | build = "build.rs" 13 | 14 | [dependencies] 15 | anyhow = "1.0" 16 | clap = { version = "4.5", features = ["derive"] } 17 | futures = "0.3" 18 | indicatif = "0.17" 19 | num_cpus = "1.16" 20 | reqwest = { version = "0.12", default-features = false, features = [ 21 | "charset", 22 | "http2", 23 | "macos-system-configuration", 24 | "rustls-tls" 25 | ] } 26 | serde = { version = "1.0", features = ["derive"] } 27 | serde_json = "1.0" 28 | thiserror = "2.0" 29 | tokio = { version = "1.44", features = ["macros", "rt-multi-thread"] } 30 | tracing = "0.1" 31 | tracing-subscriber = { version = "0.3" } 32 | 33 | [build-dependencies] 34 | clap = { version = "4.5", features = ["derive"] } 35 | clap_complete = "4.5" 36 | 37 | [lints.clippy] 38 | cargo = "warn" 39 | complexity = "warn" 40 | correctness = "deny" 41 | pedantic = "warn" 42 | perf = "warn" 43 | style = "warn" 44 | suspicious = "deny" 45 | 46 | [lints.rust] 47 | unsafe_code = "forbid" 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nix-forecast 2 | 3 | Check the forecast for today's Nix builds with a blazingly fast (🚀🔥🦀) CLI 4 | 5 | ## What is it? 6 | 7 | `nix-forecast` helps you find out what's cached in cache.nixos.org -- or any binary cache of your choosing! 8 | 9 | It does this in a few different ways: 10 | 11 | - When an installable is passed, the configured binary caches will be checked to see if they have it 12 | - When a system or home-manager configuration is passed, the number of it's build dependencies that are currently cached will be reported 13 | - By default, with no arguments passed, `nix-forecast` will try it's best to calculate how much of Nixpkgs itself is cached 14 | 15 | ## Usage 16 | 17 | ``` 18 | Check the forecast for today's Nix builds 19 | 20 | Usage: nix-forecast [OPTIONS] [INSTALLABLES]... 21 | 22 | Arguments: 23 | [INSTALLABLES]... A list of Nix installables to look for. If not given, all paths in nixpkgs are checked 24 | 25 | Options: 26 | -c, --configuration Flake reference pointing to a NixOS or nix-darwin configuration 27 | -o, --home Flake reference pointing to a standalone home-manager configuration 28 | -b, --binary-caches URLs of the substituters to check (can be passed more than once) [default: https://cache.nixos.org] 29 | -f, --flake Flake reference of nixpkgs (or other package repository) [default: nixpkgs] 30 | -s, --show-missing Show a list of store paths not found in the substituter 31 | -h, --help Print help 32 | -V, --version Print version 33 | ``` 34 | 35 | ## Examples 36 | 37 | ### Flake installables 38 | 39 | ```sh 40 | nix-forecast nixpkgs#{hello,gcc,clang,nrr} 41 | ``` 42 | 43 | ### NixOS configuration 44 | 45 | 46 | ```sh 47 | nix-forecast -c ".#nixosConfigurations.myMachine" 48 | ``` 49 | 50 | ### nix-darwin configuration 51 | 52 | ```sh 53 | nix-forecast -c ".#darwinConfigurations.myMac" 54 | ``` 55 | 56 | ### As a Lix subcommand 57 | 58 | > [!NOTE] 59 | > Requires Lix >= 2.93 60 | 61 | ```sh 62 | lix forecast nixpkgs#hello 63 | ``` 64 | 65 | ## Why? 66 | 67 | Finding out if paths are cached can be a bit troublesome in Nix, with commands like `nix build --dry-run` 68 | being the only solution a lot of the time. Meanwhile in the world of [Guix](https://guix.gnu.org/), they have 69 | had the `guix weather` command to do this for ages! This project aims to bring the power of that command right 70 | to Nix 71 | 72 | ### What about `nix-weather`? 73 | 74 | `nix-weather` is another project with a similar goal of bringing some of the features of `guix weather` to 75 | Nix. However, it does introduce it's own spin on things and has much more of a focus on NixOS configurations. 76 | In contrast, `nix-forecast` aims to be as close to the Guix command as possible, while also introducing more 77 | generic support for [Flake](https://nix.dev/concepts/flakes) references, NixOS *and* nix-darwin 78 | configurations, better error messages, and a (subjectively) more comfortable interface 79 | 80 | I've also made it slightly faster :p 81 | 82 | ``` 83 | $ hyperfine --warmup 1 './result/bin/nix-weather --name glados --config ~/flake' './target/release/nix-forecast --configuration ~/flake#nixosConfigurations.glados' 84 | Benchmark 1: ./result/bin/nix-weather --name glados --config ~/flake 85 | Time (mean ± σ): 11.387 s ± 0.646 s [User: 3.422 s, System: 1.663 s] 86 | Range (min … max): 10.490 s … 12.569 s 10 runs 87 | 88 | Benchmark 2: ./target/release/nix-forecast --configuration ~/flake#nixosConfigurations.glados 89 | Time (mean ± σ): 6.395 s ± 0.229 s [User: 0.992 s, System: 0.967 s] 90 | Range (min … max): 6.231 s … 7.030 s 10 runs 91 | 92 | Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options. 93 | 94 | Summary 95 | ./target/release/nix-forecast --configuration ~/flake#nixosConfigurations.glados ran 96 | 1.78 ± 0.12 times faster than ./result/bin/nix-weather --name glados --config ~/flake 97 | ``` 98 | 99 | ## Inspired by... 100 | 101 | - [guix weather](https://guix.gnu.org/manual/en/html_node/Invoking-guix-weather.html) 102 | - [nix-weather](https://github.com/cafkafk/nix-weather/) 103 | - [My much slower version in Fish](https://discourse.nixos.org/t/how-to-find-uncached-dependencies-of-a-closure/45385) 104 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::io::Error; 2 | 3 | use clap::{CommandFactory, ValueEnum}; 4 | use clap_complete::{generate_to, Shell}; 5 | 6 | include!("src/cli.rs"); 7 | 8 | fn main() -> Result<(), Error> { 9 | let out_dir = if let Some(completion_dir) = option_env!("COMPLETION_DIR") { 10 | std::fs::create_dir_all(completion_dir)?; 11 | completion_dir.to_string() 12 | } else if let Ok(out_dir) = std::env::var("OUT_DIR") { 13 | out_dir 14 | } else { 15 | println!("cargo:warning=Unable to resolve `COMPLETION_DIR` or `OUT_DIR` in environment. Completions will not be built"); 16 | return Ok(()); 17 | }; 18 | 19 | let mut command = Cli::command(); 20 | for &shell in Shell::value_variants() { 21 | generate_to(shell, &mut command, env!("CARGO_PKG_NAME"), &out_dir)?; 22 | } 23 | 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? import nixpkgs { 3 | inherit system; 4 | config = { }; 5 | overlays = [ ]; 6 | }, 7 | lib ? pkgs.lib, 8 | nixpkgs ? , 9 | system ? builtins.currentSystem, 10 | }: 11 | 12 | let 13 | nixForecastPackages = lib.makeScope pkgs.newScope (lib.flip (import ./overlay.nix) pkgs); 14 | in 15 | { 16 | inherit (nixForecastPackages) nix-forecast; 17 | } 18 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1746576598, 6 | "narHash": "sha256-FshoQvr6Aor5SnORVvh/ZdJ1Sa2U4ZrIMwKBX5k2wu0=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "b3582c75c7f21ce0b429898980eddbbf05c68e55", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "nixpkgs-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Check the forecast for today's Nix builds"; 3 | 4 | inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 5 | 6 | outputs = 7 | { 8 | self, 9 | nixpkgs, 10 | }: 11 | let 12 | inherit (nixpkgs) lib; 13 | 14 | # We want to generate outputs for as many systems as possible, 15 | # even if we don't officially support or test for them 16 | allSystems = lib.systems.flakeExposed; 17 | 18 | # These are the systems we do officially support and test, though 19 | supportedSystems = [ 20 | "x86_64-linux" 21 | "aarch64-linux" 22 | "x86_64-darwin" 23 | "aarch64-darwin" 24 | ]; 25 | 26 | forAllSystems = lib.genAttrs allSystems; 27 | nixpkgsFor = nixpkgs.legacyPackages; 28 | in 29 | { 30 | checks = forAllSystems ( 31 | system: 32 | let 33 | pkgs = nixpkgsFor.${system}; 34 | packages = self.packages.${system}; 35 | 36 | mkCheck = 37 | name: deps: script: 38 | pkgs.runCommand name { nativeBuildInputs = deps; } '' 39 | ${script} 40 | touch $out 41 | ''; 42 | in 43 | lib.optionalAttrs (lib.elem system supportedSystems) { 44 | clippy = packages.nix-forecast.overrideAttrs { 45 | pname = "check-clippy"; 46 | 47 | nativeBuildInputs = [ 48 | pkgs.cargo 49 | pkgs.clippy 50 | pkgs.clippy-sarif 51 | pkgs.rustPlatform.cargoSetupHook 52 | pkgs.rustc 53 | pkgs.sarif-fmt 54 | ]; 55 | 56 | buildPhase = '' 57 | runHook preBuild 58 | cargo clippy \ 59 | --all-features \ 60 | --all-targets \ 61 | --tests \ 62 | --message-format=json \ 63 | | clippy-sarif | tee $out | sarif-fmt 64 | runHook postBuild 65 | ''; 66 | 67 | dontInstall = true; 68 | doCheck = false; 69 | doInstallCheck = false; 70 | dontFixup = true; 71 | 72 | passthru = { }; 73 | meta = { }; 74 | }; 75 | 76 | rustfmt = mkCheck "check-cargo-fmt" [ 77 | pkgs.cargo 78 | pkgs.rustfmt 79 | ] "cd ${self} && cargo fmt -- --check"; 80 | 81 | actionlint = mkCheck "check-actionlint" [ 82 | pkgs.actionlint 83 | ] "actionlint ${self}/.github/workflows/*"; 84 | 85 | deadnix = mkCheck "check-deadnix" [ pkgs.deadnix ] "deadnix --fail ${self}"; 86 | 87 | nixfmt = mkCheck "check-nixfmt" [ pkgs.nixfmt-rfc-style ] "nixfmt --check ${self}"; 88 | 89 | statix = mkCheck "check-statix" [ pkgs.statix ] "statix check ${self}"; 90 | } 91 | ); 92 | 93 | devShells = forAllSystems ( 94 | system: 95 | let 96 | pkgs = nixpkgsFor.${system}; 97 | in 98 | lib.optionalAttrs (lib.elem system supportedSystems) { 99 | default = pkgs.mkShell { 100 | packages = [ 101 | # Rust tools 102 | pkgs.clippy 103 | pkgs.rust-analyzer 104 | pkgs.rustfmt 105 | 106 | # Nix tools 107 | self.formatter.${system} 108 | pkgs.nil 109 | pkgs.statix 110 | ]; 111 | 112 | env = { 113 | RUST_SRC_PATH = toString pkgs.rustPlatform.rustLibSrc; 114 | }; 115 | 116 | inputsFrom = [ self.packages.${system}.nix-forecast ]; 117 | }; 118 | } 119 | ); 120 | 121 | formatter = forAllSystems (system: nixpkgsFor.${system}.nixfmt-rfc-style); 122 | 123 | legacyPackages = forAllSystems (system: { 124 | nix-forecast-debug = self.packages.${system}.nix-forecast.overrideAttrs ( 125 | finalAttrs: _: { 126 | cargoBuildType = "debug"; 127 | cargoCheckType = finalAttrs.cargoBuildType; 128 | } 129 | ); 130 | }); 131 | 132 | packages = forAllSystems ( 133 | system: 134 | let 135 | pkgs = nixpkgsFor.${system}; 136 | pkgs' = import ./default.nix { inherit pkgs; }; 137 | in 138 | pkgs' // { default = pkgs'.nix-forecast; } 139 | ); 140 | 141 | overlays.default = final: prev: import ./overlay.nix final prev; 142 | }; 143 | } 144 | -------------------------------------------------------------------------------- /nix/package.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | installShellFiles, 4 | makeBinaryWrapper, 5 | nix, 6 | rustPlatform, 7 | versionCheckHook, 8 | }: 9 | 10 | let 11 | fs = lib.fileset; 12 | in 13 | rustPlatform.buildRustPackage rec { 14 | pname = "nix-forecast"; 15 | inherit (passthru.cargoTOML.package) version; 16 | 17 | src = fs.toSource { 18 | root = ../.; 19 | fileset = fs.intersection (fs.gitTracked ../.) ( 20 | fs.unions [ 21 | ../Cargo.lock 22 | ../Cargo.toml 23 | ../build.rs 24 | ../src 25 | ] 26 | ); 27 | }; 28 | 29 | cargoLock.lockFile = ../Cargo.lock; 30 | 31 | nativeBuildInputs = [ 32 | installShellFiles 33 | makeBinaryWrapper 34 | ]; 35 | 36 | doInstallCheck = true; 37 | nativeInstallCheckInputs = [ versionCheckHook ]; 38 | 39 | # NOTE: Yes, we specifically need Nix. Lix does not have the newer 40 | # `path-info --json` output used internally 41 | postInstall = '' 42 | wrapProgram $out/bin/nix-forecast --prefix PATH : ${lib.makeBinPath [ nix ]} 43 | 44 | installShellCompletion --cmd nix-forecast \ 45 | --bash completions/nix-forecast.bash \ 46 | --fish completions/nix-forecast.fish \ 47 | --zsh completions/_nix-forecast 48 | 49 | ln -s $out/bin/{n,l}ix-forecast 50 | ''; 51 | 52 | env = { 53 | COMPLETION_DIR = "completions"; 54 | }; 55 | 56 | passthru = { 57 | cargoTOML = lib.importTOML ../Cargo.toml; 58 | }; 59 | 60 | meta = { 61 | description = "Check the forecast for today's Nix builds"; 62 | homepage = "https://github.com/getchoo/nix-forecast"; 63 | changelog = "https://github.com/getchoo/nix-forecast/releases/tag/${version}"; 64 | license = lib.licenses.mpl20; 65 | maintainers = with lib.maintainers; [ getchoo ]; 66 | mainProgram = "nix-forecast"; 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /overlay.nix: -------------------------------------------------------------------------------- 1 | final: _: { 2 | nix-forecast = final.callPackage ./nix/package.nix { }; 3 | } 4 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | 3 | #[derive(Clone, Debug, Parser)] 4 | #[command(version, about, long_about = None)] 5 | pub struct Cli { 6 | /// A list of Nix installables to look for. If not given, all paths in nixpkgs are checked 7 | pub installables: Option>, 8 | 9 | /// Flake reference pointing to a NixOS or nix-darwin configuration 10 | #[allow(clippy::doc_markdown)] // Why does "NixOS" trigger this??? 11 | #[arg(short, long, conflicts_with("installables"))] 12 | pub configuration: Option, 13 | 14 | /// Flake reference pointing to a standalone home-manager configuration 15 | #[arg( 16 | short = 'o', 17 | long, 18 | conflicts_with("configuration"), 19 | conflicts_with("installables") 20 | )] 21 | pub home: Option, 22 | 23 | /// URLs of the substituters to check (can be passed more than once) 24 | #[arg( 25 | alias = "binary-cache", 26 | short, 27 | long, 28 | default_value = "https://cache.nixos.org" 29 | )] 30 | pub binary_caches: Vec, 31 | 32 | /// Flake reference of nixpkgs (or other package repository) 33 | #[arg(short, long, default_value = "nixpkgs")] 34 | pub flake: String, 35 | 36 | /// Show a list of store paths not found in the substituter 37 | #[arg(short, long)] 38 | pub show_missing: bool, 39 | } 40 | -------------------------------------------------------------------------------- /src/command.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | http::{self, Ext}, 3 | nix, 4 | }; 5 | 6 | use anyhow::Result; 7 | use futures::{stream, StreamExt, TryStreamExt}; 8 | use indicatif::{ProgressBar, ProgressStyle}; 9 | use tracing::instrument; 10 | 11 | pub trait Run { 12 | async fn run(&self) -> Result<()>; 13 | } 14 | 15 | impl Run for crate::Cli { 16 | #[instrument(skip(self))] 17 | async fn run(&self) -> Result<()> { 18 | let store_paths = if let Some(installables) = self.installables.clone() { 19 | resolve_installables(installables).await? 20 | } else if let Some(configuration) = &self.configuration { 21 | eprintln!("❓ Indexing requisites of configuration closure"); 22 | nix::system_configuration_closure_paths(configuration)? 23 | } else if let Some(home) = &self.home { 24 | eprintln!("❓ Indexing requisites of home configuration closure"); 25 | nix::home_configuration_closure_paths(home)? 26 | } else { 27 | eprintln!("❓ Indexing all installables of flake `{}`", self.flake); 28 | let installables = nix::all_flake_installables(&self.flake)?; 29 | resolve_installables(installables).await? 30 | }; 31 | 32 | check_store_paths(&self.binary_caches, &store_paths, self.show_missing).await?; 33 | 34 | Ok(()) 35 | } 36 | } 37 | 38 | #[instrument(skip(installables))] 39 | async fn resolve_installables(installables: Vec) -> Result> { 40 | eprintln!( 41 | "🔃 Attempting to evaluate {} installable(s)", 42 | installables.len() 43 | ); 44 | 45 | // Find our outputs concurrently 46 | let progress_bar = ProgressBar::new(installables.len() as u64).with_style(progress_style()?); 47 | let out_paths: Vec = stream::iter(&installables) 48 | .map(|installable| { 49 | let progress_bar = &progress_bar; 50 | async move { 51 | progress_bar.inc(1); 52 | let out_path = nix::out_path(installable)?; 53 | 54 | anyhow::Ok(out_path) 55 | } 56 | }) 57 | .buffer_unordered(num_cpus::get()) // try not to explode computers 58 | .try_collect() 59 | .await?; 60 | 61 | eprintln!("✅ Evaluated {} installable(s)!", out_paths.len()); 62 | 63 | Ok(out_paths) 64 | } 65 | 66 | #[allow(clippy::cast_precision_loss)] 67 | #[instrument(skip(store_paths))] 68 | async fn check_store_paths( 69 | binary_caches: &Vec, 70 | store_paths: &Vec, 71 | show_missing: bool, 72 | ) -> Result<()> { 73 | let num_store_paths = store_paths.len(); 74 | eprintln!("🌡️ Checking for {num_store_paths} store path(s) in: {binary_caches:?}"); 75 | 76 | let http = ::default(); 77 | let progress_bar = ProgressBar::new(num_store_paths as u64).with_style(progress_style()?); 78 | let uncached_paths: Vec<&str> = stream::iter(store_paths) 79 | // Check the cache for all of our paths 80 | .map(|store_path| { 81 | let http = &http; 82 | let progress_bar = &progress_bar; 83 | async move { 84 | let mut has_store_path = false; 85 | for binary_cache in binary_caches { 86 | if http.has_store_path(binary_cache, store_path).await? { 87 | has_store_path = true; 88 | } 89 | } 90 | progress_bar.inc(1); 91 | 92 | anyhow::Ok((has_store_path, store_path.as_str())) 93 | } 94 | }) 95 | .buffer_unordered(100) 96 | // Filter out misses 97 | .try_filter_map(|(has_store_path, store_path)| async move { 98 | Ok((!has_store_path).then_some(store_path)) 99 | }) 100 | .try_collect() 101 | .await?; 102 | 103 | let num_uncached = uncached_paths.len(); 104 | let num_cached = num_store_paths - num_uncached; 105 | 106 | eprintln!( 107 | "☀️ {:.2}% of paths available ({} out of {})", 108 | (num_cached as f32 / num_store_paths as f32) * 100.0, 109 | num_cached, 110 | num_store_paths, 111 | ); 112 | 113 | if show_missing { 114 | eprintln!("\n⛈️ Found {num_uncached} uncached paths:"); 115 | println!("{}", uncached_paths.join("\n")); 116 | } 117 | 118 | Ok(()) 119 | } 120 | 121 | pub fn progress_style() -> Result { 122 | Ok( 123 | ProgressStyle::with_template("[{elapsed_precise}] {bar:40} {pos:>7}/{len:7} {msg}")? 124 | .progress_chars("##-"), 125 | ) 126 | } 127 | -------------------------------------------------------------------------------- /src/http.rs: -------------------------------------------------------------------------------- 1 | /// HTTP interfaces 2 | use crate::Error; 3 | 4 | use anyhow::Result; 5 | use reqwest::{Method, StatusCode}; 6 | use tracing::{event, instrument, Level}; 7 | 8 | // https://nix.dev/manual/nix/2.24/store/store-path 9 | const STORE_DIRECTORY: &str = "/nix/store"; 10 | const DIGEST_SIZE: usize = 32; 11 | 12 | pub type Client = reqwest::Client; 13 | 14 | pub trait Ext { 15 | fn default() -> Self; 16 | async fn has_store_path(&self, binary_cache_url: &str, store_path: &str) -> Result; 17 | } 18 | 19 | impl Ext for Client { 20 | /// Create a client with our user agent 21 | fn default() -> Self { 22 | Client::builder() 23 | .user_agent(concat!( 24 | env!("CARGO_PKG_NAME"), 25 | "/", 26 | env!("CARGO_PKG_VERSION") 27 | )) 28 | .build() 29 | .unwrap() 30 | } 31 | 32 | /// Check if a store path is available in the given binary cache 33 | #[instrument(skip(self, binary_cache_url, store_path))] 34 | async fn has_store_path(&self, binary_cache_url: &str, store_path: &str) -> Result { 35 | let url = format!( 36 | "{binary_cache_url}/{}.narinfo", 37 | hash_from_store_path(store_path) 38 | ); 39 | 40 | let request = self.request(Method::HEAD, url).build()?; 41 | event!( 42 | Level::TRACE, 43 | "Checking for store path {store_path} in binary cache at {}", 44 | request.url() 45 | ); 46 | let response = self.execute(request).await?; 47 | 48 | match response.status() { 49 | StatusCode::OK => { 50 | event!( 51 | Level::TRACE, 52 | "Found store path `{store_path}` in binary cache" 53 | ); 54 | Ok(true) 55 | } 56 | StatusCode::NOT_FOUND => { 57 | event!( 58 | Level::TRACE, 59 | "Did not find store path `{store_path}` in binary cache" 60 | ); 61 | Ok(false) 62 | } 63 | status_code => Err(Error::HTTPFailed(status_code).into()), 64 | } 65 | } 66 | } 67 | 68 | /// Strip the from /nix/store/- 69 | fn hash_from_store_path(store_path: &str) -> &str { 70 | // Store paths will always start with the store directory, followed by a path separator. See 71 | // the above link 72 | let start_index = STORE_DIRECTORY.len() + 1; 73 | // The next DIGEST_SIZE characters will then be the digest 74 | let end_index = start_index + DIGEST_SIZE; 75 | 76 | &store_path[start_index..end_index] 77 | } 78 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::multiple_crate_versions)] 2 | use anyhow::Result; 3 | use clap::Parser; 4 | use reqwest::StatusCode; 5 | use tracing::instrument; 6 | 7 | mod cli; 8 | mod command; 9 | mod http; 10 | mod nix; 11 | 12 | use cli::Cli; 13 | use command::Run; 14 | 15 | #[derive(Clone, Debug, thiserror::Error)] 16 | enum Error { 17 | #[error("Unstable to complete HTTP request: {0}")] 18 | HTTPFailed(StatusCode), 19 | #[error("Nix exited with code {code}: {stderr}")] 20 | Nix { code: i32, stderr: String }, 21 | } 22 | 23 | #[tokio::main] 24 | #[instrument] 25 | async fn main() -> Result<()> { 26 | tracing_subscriber::fmt::init(); 27 | 28 | let cli = Cli::parse(); 29 | if let Err(why) = cli.run().await { 30 | eprintln!("{why}"); 31 | } 32 | 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /src/nix.rs: -------------------------------------------------------------------------------- 1 | /// Abstractions over Nix's CLI 2 | use crate::Error; 3 | 4 | use std::{collections::HashMap, process::Command}; 5 | 6 | use anyhow::{Context, Result}; 7 | use serde::{Deserialize, Serialize}; 8 | use serde_json::Value; 9 | use tracing::{event, instrument, Level}; 10 | 11 | /// JSON output of `nix build` 12 | #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] 13 | #[serde(rename_all = "camelCase")] 14 | struct Build { 15 | drv_path: String, 16 | /// Derivation output names and their path 17 | outputs: HashMap, 18 | } 19 | 20 | /// JSON output of `nix path-info` pre Nix 2.19 21 | #[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Deserialize, Serialize)] 22 | #[serde(rename_all = "camelCase")] 23 | struct PathInfo { 24 | path: String, 25 | } 26 | 27 | /// Strip derivations from a list of store paths 28 | fn strip_drvs(paths: Vec) -> Vec { 29 | paths 30 | .into_iter() 31 | .filter(|path| { 32 | std::path::Path::new(path) 33 | .extension() 34 | .is_some_and(|ext| !ext.eq_ignore_ascii_case("drv")) 35 | }) 36 | .collect() 37 | } 38 | 39 | #[instrument(skip(installable))] 40 | pub fn dry_build_output(installable: &str) -> Result> { 41 | event!(Level::TRACE, "Running command `nix build --extra-experimental-features 'nix-command flakes' --dry-run --json {installable}`"); 42 | let output = Command::new("nix") 43 | .args([ 44 | "--extra-experimental-features", 45 | "nix-command flakes", 46 | "build", 47 | "--dry-run", 48 | "--json", 49 | installable, 50 | ]) 51 | .output()?; 52 | 53 | if output.status.success() { 54 | Ok(output.stdout) 55 | } else { 56 | let code = output.status.code().unwrap_or(1); 57 | let stderr = String::from_utf8(output.stderr.clone()).unwrap_or_default(); 58 | 59 | Err(Error::Nix { code, stderr }.into()) 60 | } 61 | } 62 | 63 | /// Get the `outPath` (store path) of an installable 64 | #[instrument(skip(installable))] 65 | pub fn out_path(installable: &str) -> Result { 66 | let dry_build_output = dry_build_output(installable)?; 67 | let data: Vec = serde_json::from_slice(&dry_build_output)?; 68 | 69 | let out_path = data 70 | .first() 71 | .context("Unable to parse `nix build` output!")? 72 | .outputs 73 | .get("out") 74 | .with_context(|| format!("Unable to find output `out` for installable {installable}"))?; 75 | 76 | Ok(out_path.to_string()) 77 | } 78 | 79 | /// Get the `drvPath` (derivation path) of an installable 80 | #[instrument(skip(installable))] 81 | pub fn drv_path(installable: &str) -> Result { 82 | let dry_build_output = dry_build_output(installable)?; 83 | let data: Vec = serde_json::from_slice(&dry_build_output)?; 84 | 85 | let drv_path = &data 86 | .first() 87 | .context("Unable to parse `nix build` output!")? 88 | .drv_path; 89 | 90 | Ok(drv_path.to_string()) 91 | } 92 | 93 | /// Get all paths in a closure at the given store path 94 | #[instrument(skip(store_path))] 95 | pub fn closure_paths(store_path: &str, with_outputs: bool) -> Result> { 96 | event!(Level::TRACE, "Querying closure paths"); 97 | 98 | let mut args = vec!["--query", "--requisites", store_path]; 99 | 100 | if with_outputs { 101 | args.push("--include-outputs"); 102 | } 103 | 104 | let output = Command::new("nix-store").args(args).output()?; 105 | 106 | if output.status.success() { 107 | // Capture paths from command output, strip drvs 108 | let stdout = String::from_utf8(output.stdout)?; 109 | let paths = stdout.lines().map(ToString::to_string).collect(); 110 | 111 | Ok(paths) 112 | } else { 113 | let code = output.status.code().unwrap_or(1); 114 | let stderr = String::from_utf8(output.stderr.clone()).unwrap_or_default(); 115 | 116 | Err(Error::Nix { code, stderr }.into()) 117 | } 118 | } 119 | 120 | /// Get all paths in an installable's closure 121 | #[instrument(skip(installable))] 122 | fn installable_closure_paths(installable: &str) -> Result> { 123 | let store_path = drv_path(installable)?; 124 | let paths = closure_paths(&store_path, true)?; 125 | let out_paths = strip_drvs(paths); 126 | 127 | Ok(out_paths) 128 | } 129 | 130 | /// Get all paths in a NixOS or nix-darwin configuration's closure 131 | #[instrument(skip(configuration_ref))] 132 | pub fn system_configuration_closure_paths(configuration_ref: &str) -> Result> { 133 | let installable = format!("{configuration_ref}.config.system.build.toplevel"); 134 | let out_paths = installable_closure_paths(&installable)?; 135 | 136 | Ok(out_paths) 137 | } 138 | 139 | /// Get all paths in a home-manager configuration's closure 140 | #[instrument(skip(configuration_ref))] 141 | pub fn home_configuration_closure_paths(configuration_ref: &str) -> Result> { 142 | let installable = format!("{configuration_ref}.activationPackage"); 143 | let out_paths = installable_closure_paths(&installable)?; 144 | 145 | Ok(out_paths) 146 | } 147 | 148 | /// Get all installables available in a given Flake 149 | #[instrument(skip(flake_ref))] 150 | pub fn all_flake_installables(flake_ref: &str) -> Result> { 151 | event!( 152 | Level::TRACE, 153 | "Running command `nix --extra-experimental-features 'nix-command flakes' search --json {flake_ref} .`" 154 | ); 155 | let output = Command::new("nix") 156 | .args([ 157 | "--extra-experimental-features", 158 | "nix-command flakes", 159 | "search", 160 | "--json", 161 | flake_ref, 162 | ".", 163 | ]) 164 | .output()?; 165 | 166 | if output.status.success() { 167 | let search_results: HashMap = serde_json::from_slice(&output.stdout)?; 168 | let package_names = search_results 169 | .keys() 170 | .map(|name| format!("{flake_ref}#{name}")) 171 | .collect::>(); 172 | 173 | Ok(package_names) 174 | } else { 175 | let code = output.status.code().unwrap_or(1); 176 | let stderr = String::from_utf8(output.stderr.clone()).unwrap_or_default(); 177 | 178 | Err(Error::Nix { code, stderr }.into()) 179 | } 180 | } 181 | --------------------------------------------------------------------------------