├── .github └── workflows │ └── main.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── docs └── value-literals.md ├── rust-toolchain └── src ├── command.rs ├── command ├── parser.rs └── tokenizer.rs ├── evaluator.rs ├── main.rs ├── runtime.rs └── wit.rs /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: {} 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout the source code 13 | uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 1 16 | submodules: recursive 17 | 18 | - name: Installs nightly toolchain 19 | run: | 20 | rustup update nightly 21 | rustup component add rustfmt --toolchain nightly 22 | rustup default nightly 23 | 24 | - name: Formatting check 25 | run: cargo fmt --all -- --check 26 | 27 | - name: Run tests 28 | run: cargo test --all 29 | 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDEs 2 | .idea/ 3 | 4 | # Rust 5 | /target 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | 36 | ## Attribution 37 | 38 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 39 | 40 | [homepage]: https://www.contributor-covenant.org 41 | [version]: https://www.contributor-covenant.org/version/1/4/ 42 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 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 = "ahash" 22 | version = "0.8.11" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 25 | dependencies = [ 26 | "cfg-if", 27 | "once_cell", 28 | "version_check", 29 | "zerocopy", 30 | ] 31 | 32 | [[package]] 33 | name = "aho-corasick" 34 | version = "1.1.2" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 37 | dependencies = [ 38 | "memchr", 39 | ] 40 | 41 | [[package]] 42 | name = "ambient-authority" 43 | version = "0.0.2" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" 46 | 47 | [[package]] 48 | name = "android_system_properties" 49 | version = "0.1.5" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 52 | dependencies = [ 53 | "libc", 54 | ] 55 | 56 | [[package]] 57 | name = "anstream" 58 | version = "0.6.11" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" 61 | dependencies = [ 62 | "anstyle", 63 | "anstyle-parse", 64 | "anstyle-query", 65 | "anstyle-wincon", 66 | "colorchoice", 67 | "utf8parse", 68 | ] 69 | 70 | [[package]] 71 | name = "anstyle" 72 | version = "1.0.4" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" 75 | 76 | [[package]] 77 | name = "anstyle-parse" 78 | version = "0.2.3" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" 81 | dependencies = [ 82 | "utf8parse", 83 | ] 84 | 85 | [[package]] 86 | name = "anstyle-query" 87 | version = "1.0.2" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" 90 | dependencies = [ 91 | "windows-sys 0.52.0", 92 | ] 93 | 94 | [[package]] 95 | name = "anstyle-wincon" 96 | version = "3.0.2" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" 99 | dependencies = [ 100 | "anstyle", 101 | "windows-sys 0.52.0", 102 | ] 103 | 104 | [[package]] 105 | name = "anyhow" 106 | version = "1.0.79" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" 109 | 110 | [[package]] 111 | name = "arbitrary" 112 | version = "1.3.2" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" 115 | 116 | [[package]] 117 | name = "async-trait" 118 | version = "0.1.77" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" 121 | dependencies = [ 122 | "proc-macro2", 123 | "quote", 124 | "syn", 125 | ] 126 | 127 | [[package]] 128 | name = "autocfg" 129 | version = "1.1.0" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 132 | 133 | [[package]] 134 | name = "backtrace" 135 | version = "0.3.69" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" 138 | dependencies = [ 139 | "addr2line", 140 | "cc", 141 | "cfg-if", 142 | "libc", 143 | "miniz_oxide", 144 | "object 0.32.2", 145 | "rustc-demangle", 146 | ] 147 | 148 | [[package]] 149 | name = "base64" 150 | version = "0.21.5" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" 153 | 154 | [[package]] 155 | name = "bitflags" 156 | version = "1.3.2" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 159 | 160 | [[package]] 161 | name = "bitflags" 162 | version = "2.4.1" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" 165 | 166 | [[package]] 167 | name = "bitmaps" 168 | version = "2.1.0" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" 171 | dependencies = [ 172 | "typenum", 173 | ] 174 | 175 | [[package]] 176 | name = "block-buffer" 177 | version = "0.10.4" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 180 | dependencies = [ 181 | "generic-array", 182 | ] 183 | 184 | [[package]] 185 | name = "bumpalo" 186 | version = "3.14.0" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" 189 | 190 | [[package]] 191 | name = "bytecount" 192 | version = "0.6.7" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" 195 | 196 | [[package]] 197 | name = "byteorder" 198 | version = "1.5.0" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 201 | 202 | [[package]] 203 | name = "bytes" 204 | version = "1.6.0" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" 207 | 208 | [[package]] 209 | name = "cap-fs-ext" 210 | version = "3.1.0" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "2fc2d2954524be4866aaa720f008fba9995de54784957a1b0e0119992d6d5e52" 213 | dependencies = [ 214 | "cap-primitives", 215 | "cap-std", 216 | "io-lifetimes", 217 | "windows-sys 0.52.0", 218 | ] 219 | 220 | [[package]] 221 | name = "cap-net-ext" 222 | version = "3.1.0" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "799c81d79ea9c71a1438efd417c788214bc9e7986046d3710b6bbe60da4d8275" 225 | dependencies = [ 226 | "cap-primitives", 227 | "cap-std", 228 | "rustix", 229 | "smallvec", 230 | ] 231 | 232 | [[package]] 233 | name = "cap-primitives" 234 | version = "3.1.0" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "00172660727e2d7f808e7cc2bfffd093fdb3ea2ff2ef819289418a3c3ffab5ac" 237 | dependencies = [ 238 | "ambient-authority", 239 | "fs-set-times", 240 | "io-extras", 241 | "io-lifetimes", 242 | "ipnet", 243 | "maybe-owned", 244 | "rustix", 245 | "windows-sys 0.52.0", 246 | "winx", 247 | ] 248 | 249 | [[package]] 250 | name = "cap-rand" 251 | version = "3.1.0" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "270f1d341a2afc62604f8f688bee4e444d052b7a74c1458dd3aa7efb47d4077f" 254 | dependencies = [ 255 | "ambient-authority", 256 | "rand", 257 | ] 258 | 259 | [[package]] 260 | name = "cap-std" 261 | version = "3.1.0" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "8cd9187bb3f7478a4c135ea10473a41a5f029d2ac800c1adf64f35ec7d4c8603" 264 | dependencies = [ 265 | "cap-primitives", 266 | "io-extras", 267 | "io-lifetimes", 268 | "rustix", 269 | ] 270 | 271 | [[package]] 272 | name = "cap-time-ext" 273 | version = "3.1.0" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "91666f31e30c85b1d2ee8432c90987f752c45f5821f5638027b41e73e16a395b" 276 | dependencies = [ 277 | "ambient-authority", 278 | "cap-primitives", 279 | "iana-time-zone", 280 | "once_cell", 281 | "rustix", 282 | "winx", 283 | ] 284 | 285 | [[package]] 286 | name = "cc" 287 | version = "1.0.83" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 290 | dependencies = [ 291 | "jobserver", 292 | "libc", 293 | ] 294 | 295 | [[package]] 296 | name = "cfg-if" 297 | version = "1.0.0" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 300 | 301 | [[package]] 302 | name = "cfg_aliases" 303 | version = "0.1.1" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" 306 | 307 | [[package]] 308 | name = "clap" 309 | version = "4.5.0" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" 312 | dependencies = [ 313 | "clap_builder", 314 | "clap_derive", 315 | ] 316 | 317 | [[package]] 318 | name = "clap_builder" 319 | version = "4.5.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" 322 | dependencies = [ 323 | "anstream", 324 | "anstyle", 325 | "clap_lex", 326 | "strsim", 327 | ] 328 | 329 | [[package]] 330 | name = "clap_derive" 331 | version = "4.5.0" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" 334 | dependencies = [ 335 | "heck", 336 | "proc-macro2", 337 | "quote", 338 | "syn", 339 | ] 340 | 341 | [[package]] 342 | name = "clap_lex" 343 | version = "0.7.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" 346 | 347 | [[package]] 348 | name = "clipboard-win" 349 | version = "5.0.0" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "c57002a5d9be777c1ef967e33674dac9ebd310d8893e4e3437b14d5f0f6372cc" 352 | dependencies = [ 353 | "error-code", 354 | ] 355 | 356 | [[package]] 357 | name = "cobs" 358 | version = "0.2.3" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" 361 | 362 | [[package]] 363 | name = "colorchoice" 364 | version = "1.0.0" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 367 | 368 | [[package]] 369 | name = "colored" 370 | version = "2.1.0" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 373 | dependencies = [ 374 | "lazy_static", 375 | "windows-sys 0.48.0", 376 | ] 377 | 378 | [[package]] 379 | name = "core-foundation-sys" 380 | version = "0.8.6" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 383 | 384 | [[package]] 385 | name = "cpp_demangle" 386 | version = "0.4.3" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" 389 | dependencies = [ 390 | "cfg-if", 391 | ] 392 | 393 | [[package]] 394 | name = "cpufeatures" 395 | version = "0.2.12" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" 398 | dependencies = [ 399 | "libc", 400 | ] 401 | 402 | [[package]] 403 | name = "cranelift-bforest" 404 | version = "0.109.0" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "0b6b33d7e757a887989eb18b35712b2a67d96171ec3149d1bfb657b29b7b367c" 407 | dependencies = [ 408 | "cranelift-entity", 409 | ] 410 | 411 | [[package]] 412 | name = "cranelift-codegen" 413 | version = "0.109.0" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "b9acf15cb22be42d07c3b57d7856329cb228b7315d385346149df2566ad5e4aa" 416 | dependencies = [ 417 | "bumpalo", 418 | "cranelift-bforest", 419 | "cranelift-codegen-meta", 420 | "cranelift-codegen-shared", 421 | "cranelift-control", 422 | "cranelift-entity", 423 | "cranelift-isle", 424 | "gimli", 425 | "hashbrown 0.14.3", 426 | "log", 427 | "regalloc2", 428 | "rustc-hash", 429 | "smallvec", 430 | "target-lexicon", 431 | ] 432 | 433 | [[package]] 434 | name = "cranelift-codegen-meta" 435 | version = "0.109.0" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "e934d301392b73b3f8b0540391fb82465a0f179a3cee7c726482ac4727efcc97" 438 | dependencies = [ 439 | "cranelift-codegen-shared", 440 | ] 441 | 442 | [[package]] 443 | name = "cranelift-codegen-shared" 444 | version = "0.109.0" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "8afb2a2566b3d54b854dfb288b3b187f6d3d17d6f762c92898207eba302931da" 447 | 448 | [[package]] 449 | name = "cranelift-control" 450 | version = "0.109.0" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "0100f33b704cdacd01ad66ff41f8c5030d57cbff078e2a4e49ab1822591299fa" 453 | dependencies = [ 454 | "arbitrary", 455 | ] 456 | 457 | [[package]] 458 | name = "cranelift-entity" 459 | version = "0.109.0" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "a8cfdc315e5d18997093e040a8d234bea1ac1e118a716d3e30f40d449e78207b" 462 | dependencies = [ 463 | "serde", 464 | "serde_derive", 465 | ] 466 | 467 | [[package]] 468 | name = "cranelift-frontend" 469 | version = "0.109.0" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "0f74b84f16af2e982b0c0c72233503d9d55cbfe3865dbe807ca28dc6642a28b5" 472 | dependencies = [ 473 | "cranelift-codegen", 474 | "log", 475 | "smallvec", 476 | "target-lexicon", 477 | ] 478 | 479 | [[package]] 480 | name = "cranelift-isle" 481 | version = "0.109.0" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "adf306d3dde705fb94bd48082f01d38c4ededc74293a4c007805f610bf08bc6e" 484 | 485 | [[package]] 486 | name = "cranelift-native" 487 | version = "0.109.0" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "1ea0ebdef7aff4a79bcbc8b6495f31315f16b3bf311152f472eaa8d679352581" 490 | dependencies = [ 491 | "cranelift-codegen", 492 | "libc", 493 | "target-lexicon", 494 | ] 495 | 496 | [[package]] 497 | name = "cranelift-wasm" 498 | version = "0.109.0" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "d549108a1942065cdbac3bb96c2952afa0e1b9a3beff4b08c4308ac72257576d" 501 | dependencies = [ 502 | "cranelift-codegen", 503 | "cranelift-entity", 504 | "cranelift-frontend", 505 | "itertools", 506 | "log", 507 | "smallvec", 508 | "wasmparser 0.209.1", 509 | "wasmtime-types", 510 | ] 511 | 512 | [[package]] 513 | name = "crc32fast" 514 | version = "1.3.2" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 517 | dependencies = [ 518 | "cfg-if", 519 | ] 520 | 521 | [[package]] 522 | name = "crossbeam-deque" 523 | version = "0.8.4" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" 526 | dependencies = [ 527 | "cfg-if", 528 | "crossbeam-epoch", 529 | "crossbeam-utils", 530 | ] 531 | 532 | [[package]] 533 | name = "crossbeam-epoch" 534 | version = "0.9.17" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" 537 | dependencies = [ 538 | "autocfg", 539 | "cfg-if", 540 | "crossbeam-utils", 541 | ] 542 | 543 | [[package]] 544 | name = "crossbeam-utils" 545 | version = "0.8.18" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" 548 | dependencies = [ 549 | "cfg-if", 550 | ] 551 | 552 | [[package]] 553 | name = "crypto-common" 554 | version = "0.1.6" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 557 | dependencies = [ 558 | "generic-array", 559 | "typenum", 560 | ] 561 | 562 | [[package]] 563 | name = "debugid" 564 | version = "0.8.0" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" 567 | dependencies = [ 568 | "uuid", 569 | ] 570 | 571 | [[package]] 572 | name = "digest" 573 | version = "0.10.7" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 576 | dependencies = [ 577 | "block-buffer", 578 | "crypto-common", 579 | ] 580 | 581 | [[package]] 582 | name = "directories-next" 583 | version = "2.0.0" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" 586 | dependencies = [ 587 | "cfg-if", 588 | "dirs-sys-next", 589 | ] 590 | 591 | [[package]] 592 | name = "dirs" 593 | version = "4.0.0" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" 596 | dependencies = [ 597 | "dirs-sys", 598 | ] 599 | 600 | [[package]] 601 | name = "dirs-sys" 602 | version = "0.3.7" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" 605 | dependencies = [ 606 | "libc", 607 | "redox_users", 608 | "winapi", 609 | ] 610 | 611 | [[package]] 612 | name = "dirs-sys-next" 613 | version = "0.1.2" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 616 | dependencies = [ 617 | "libc", 618 | "redox_users", 619 | "winapi", 620 | ] 621 | 622 | [[package]] 623 | name = "either" 624 | version = "1.9.0" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 627 | 628 | [[package]] 629 | name = "embedded-io" 630 | version = "0.4.0" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" 633 | 634 | [[package]] 635 | name = "encoding_rs" 636 | version = "0.8.33" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" 639 | dependencies = [ 640 | "cfg-if", 641 | ] 642 | 643 | [[package]] 644 | name = "endian-type" 645 | version = "0.1.2" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" 648 | 649 | [[package]] 650 | name = "env_filter" 651 | version = "0.1.0" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" 654 | dependencies = [ 655 | "log", 656 | "regex", 657 | ] 658 | 659 | [[package]] 660 | name = "env_logger" 661 | version = "0.11.1" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "05e7cf40684ae96ade6232ed84582f40ce0a66efcd43a5117aef610534f8e0b8" 664 | dependencies = [ 665 | "anstream", 666 | "anstyle", 667 | "env_filter", 668 | "humantime", 669 | "log", 670 | ] 671 | 672 | [[package]] 673 | name = "equivalent" 674 | version = "1.0.1" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 677 | 678 | [[package]] 679 | name = "errno" 680 | version = "0.3.8" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 683 | dependencies = [ 684 | "libc", 685 | "windows-sys 0.52.0", 686 | ] 687 | 688 | [[package]] 689 | name = "error-code" 690 | version = "3.0.0" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc" 693 | 694 | [[package]] 695 | name = "fallible-iterator" 696 | version = "0.3.0" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 699 | 700 | [[package]] 701 | name = "fd-lock" 702 | version = "4.0.2" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" 705 | dependencies = [ 706 | "cfg-if", 707 | "rustix", 708 | "windows-sys 0.52.0", 709 | ] 710 | 711 | [[package]] 712 | name = "fixedbitset" 713 | version = "0.4.2" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" 716 | 717 | [[package]] 718 | name = "form_urlencoded" 719 | version = "1.2.1" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 722 | dependencies = [ 723 | "percent-encoding", 724 | ] 725 | 726 | [[package]] 727 | name = "fs-set-times" 728 | version = "0.20.1" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb" 731 | dependencies = [ 732 | "io-lifetimes", 733 | "rustix", 734 | "windows-sys 0.52.0", 735 | ] 736 | 737 | [[package]] 738 | name = "futures" 739 | version = "0.3.30" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 742 | dependencies = [ 743 | "futures-channel", 744 | "futures-core", 745 | "futures-io", 746 | "futures-sink", 747 | "futures-task", 748 | "futures-util", 749 | ] 750 | 751 | [[package]] 752 | name = "futures-channel" 753 | version = "0.3.30" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 756 | dependencies = [ 757 | "futures-core", 758 | "futures-sink", 759 | ] 760 | 761 | [[package]] 762 | name = "futures-core" 763 | version = "0.3.30" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 766 | 767 | [[package]] 768 | name = "futures-io" 769 | version = "0.3.30" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 772 | 773 | [[package]] 774 | name = "futures-sink" 775 | version = "0.3.30" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 778 | 779 | [[package]] 780 | name = "futures-task" 781 | version = "0.3.30" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 784 | 785 | [[package]] 786 | name = "futures-util" 787 | version = "0.3.30" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 790 | dependencies = [ 791 | "futures-core", 792 | "futures-sink", 793 | "futures-task", 794 | "pin-project-lite", 795 | "pin-utils", 796 | ] 797 | 798 | [[package]] 799 | name = "fxhash" 800 | version = "0.2.1" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 803 | dependencies = [ 804 | "byteorder", 805 | ] 806 | 807 | [[package]] 808 | name = "fxprof-processed-profile" 809 | version = "0.6.0" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" 812 | dependencies = [ 813 | "bitflags 2.4.1", 814 | "debugid", 815 | "fxhash", 816 | "serde", 817 | "serde_json", 818 | ] 819 | 820 | [[package]] 821 | name = "generic-array" 822 | version = "0.14.7" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 825 | dependencies = [ 826 | "typenum", 827 | "version_check", 828 | ] 829 | 830 | [[package]] 831 | name = "getrandom" 832 | version = "0.2.11" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" 835 | dependencies = [ 836 | "cfg-if", 837 | "libc", 838 | "wasi", 839 | ] 840 | 841 | [[package]] 842 | name = "gimli" 843 | version = "0.28.1" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 846 | dependencies = [ 847 | "fallible-iterator", 848 | "indexmap", 849 | "stable_deref_trait", 850 | ] 851 | 852 | [[package]] 853 | name = "hashbrown" 854 | version = "0.13.2" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" 857 | dependencies = [ 858 | "ahash", 859 | ] 860 | 861 | [[package]] 862 | name = "hashbrown" 863 | version = "0.14.3" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 866 | dependencies = [ 867 | "ahash", 868 | "serde", 869 | ] 870 | 871 | [[package]] 872 | name = "heck" 873 | version = "0.4.1" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 876 | 877 | [[package]] 878 | name = "hermit-abi" 879 | version = "0.3.3" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" 882 | 883 | [[package]] 884 | name = "home" 885 | version = "0.5.9" 886 | source = "registry+https://github.com/rust-lang/crates.io-index" 887 | checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" 888 | dependencies = [ 889 | "windows-sys 0.52.0", 890 | ] 891 | 892 | [[package]] 893 | name = "humantime" 894 | version = "2.1.0" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 897 | 898 | [[package]] 899 | name = "iana-time-zone" 900 | version = "0.1.60" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" 903 | dependencies = [ 904 | "android_system_properties", 905 | "core-foundation-sys", 906 | "iana-time-zone-haiku", 907 | "js-sys", 908 | "wasm-bindgen", 909 | "windows-core", 910 | ] 911 | 912 | [[package]] 913 | name = "iana-time-zone-haiku" 914 | version = "0.1.2" 915 | source = "registry+https://github.com/rust-lang/crates.io-index" 916 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 917 | dependencies = [ 918 | "cc", 919 | ] 920 | 921 | [[package]] 922 | name = "id-arena" 923 | version = "2.2.1" 924 | source = "registry+https://github.com/rust-lang/crates.io-index" 925 | checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" 926 | 927 | [[package]] 928 | name = "idna" 929 | version = "0.5.0" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 932 | dependencies = [ 933 | "unicode-bidi", 934 | "unicode-normalization", 935 | ] 936 | 937 | [[package]] 938 | name = "im-rc" 939 | version = "15.1.0" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" 942 | dependencies = [ 943 | "bitmaps", 944 | "rand_core", 945 | "rand_xoshiro", 946 | "sized-chunks", 947 | "typenum", 948 | "version_check", 949 | ] 950 | 951 | [[package]] 952 | name = "indexmap" 953 | version = "2.1.0" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" 956 | dependencies = [ 957 | "equivalent", 958 | "hashbrown 0.14.3", 959 | "serde", 960 | ] 961 | 962 | [[package]] 963 | name = "io-extras" 964 | version = "0.18.1" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "c301e73fb90e8a29e600a9f402d095765f74310d582916a952f618836a1bd1ed" 967 | dependencies = [ 968 | "io-lifetimes", 969 | "windows-sys 0.52.0", 970 | ] 971 | 972 | [[package]] 973 | name = "io-lifetimes" 974 | version = "2.0.3" 975 | source = "registry+https://github.com/rust-lang/crates.io-index" 976 | checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" 977 | 978 | [[package]] 979 | name = "ipnet" 980 | version = "2.9.0" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" 983 | 984 | [[package]] 985 | name = "itertools" 986 | version = "0.12.1" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 989 | dependencies = [ 990 | "either", 991 | ] 992 | 993 | [[package]] 994 | name = "itoa" 995 | version = "1.0.10" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" 998 | 999 | [[package]] 1000 | name = "ittapi" 1001 | version = "0.4.0" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "6b996fe614c41395cdaedf3cf408a9534851090959d90d54a535f675550b64b1" 1004 | dependencies = [ 1005 | "anyhow", 1006 | "ittapi-sys", 1007 | "log", 1008 | ] 1009 | 1010 | [[package]] 1011 | name = "ittapi-sys" 1012 | version = "0.4.0" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "52f5385394064fa2c886205dba02598013ce83d3e92d33dbdc0c52fe0e7bf4fc" 1015 | dependencies = [ 1016 | "cc", 1017 | ] 1018 | 1019 | [[package]] 1020 | name = "jobserver" 1021 | version = "0.1.27" 1022 | source = "registry+https://github.com/rust-lang/crates.io-index" 1023 | checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" 1024 | dependencies = [ 1025 | "libc", 1026 | ] 1027 | 1028 | [[package]] 1029 | name = "js-sys" 1030 | version = "0.3.66" 1031 | source = "registry+https://github.com/rust-lang/crates.io-index" 1032 | checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" 1033 | dependencies = [ 1034 | "wasm-bindgen", 1035 | ] 1036 | 1037 | [[package]] 1038 | name = "lazy_static" 1039 | version = "1.4.0" 1040 | source = "registry+https://github.com/rust-lang/crates.io-index" 1041 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 1042 | 1043 | [[package]] 1044 | name = "leb128" 1045 | version = "0.2.5" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" 1048 | 1049 | [[package]] 1050 | name = "libc" 1051 | version = "0.2.155" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" 1054 | 1055 | [[package]] 1056 | name = "libm" 1057 | version = "0.2.8" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" 1060 | 1061 | [[package]] 1062 | name = "libredox" 1063 | version = "0.0.1" 1064 | source = "registry+https://github.com/rust-lang/crates.io-index" 1065 | checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" 1066 | dependencies = [ 1067 | "bitflags 2.4.1", 1068 | "libc", 1069 | "redox_syscall", 1070 | ] 1071 | 1072 | [[package]] 1073 | name = "linux-raw-sys" 1074 | version = "0.4.12" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" 1077 | 1078 | [[package]] 1079 | name = "log" 1080 | version = "0.4.20" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 1083 | 1084 | [[package]] 1085 | name = "mach2" 1086 | version = "0.4.2" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" 1089 | dependencies = [ 1090 | "libc", 1091 | ] 1092 | 1093 | [[package]] 1094 | name = "maybe-owned" 1095 | version = "0.3.4" 1096 | source = "registry+https://github.com/rust-lang/crates.io-index" 1097 | checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" 1098 | 1099 | [[package]] 1100 | name = "memchr" 1101 | version = "2.7.1" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 1104 | 1105 | [[package]] 1106 | name = "memfd" 1107 | version = "0.6.4" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" 1110 | dependencies = [ 1111 | "rustix", 1112 | ] 1113 | 1114 | [[package]] 1115 | name = "memoffset" 1116 | version = "0.9.0" 1117 | source = "registry+https://github.com/rust-lang/crates.io-index" 1118 | checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" 1119 | dependencies = [ 1120 | "autocfg", 1121 | ] 1122 | 1123 | [[package]] 1124 | name = "minimal-lexical" 1125 | version = "0.2.1" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 1128 | 1129 | [[package]] 1130 | name = "miniz_oxide" 1131 | version = "0.7.1" 1132 | source = "registry+https://github.com/rust-lang/crates.io-index" 1133 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 1134 | dependencies = [ 1135 | "adler", 1136 | ] 1137 | 1138 | [[package]] 1139 | name = "mio" 1140 | version = "0.8.10" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" 1143 | dependencies = [ 1144 | "libc", 1145 | "wasi", 1146 | "windows-sys 0.48.0", 1147 | ] 1148 | 1149 | [[package]] 1150 | name = "nibble_vec" 1151 | version = "0.1.0" 1152 | source = "registry+https://github.com/rust-lang/crates.io-index" 1153 | checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" 1154 | dependencies = [ 1155 | "smallvec", 1156 | ] 1157 | 1158 | [[package]] 1159 | name = "nix" 1160 | version = "0.28.0" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" 1163 | dependencies = [ 1164 | "bitflags 2.4.1", 1165 | "cfg-if", 1166 | "cfg_aliases", 1167 | "libc", 1168 | ] 1169 | 1170 | [[package]] 1171 | name = "nom" 1172 | version = "7.1.3" 1173 | source = "registry+https://github.com/rust-lang/crates.io-index" 1174 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 1175 | dependencies = [ 1176 | "memchr", 1177 | "minimal-lexical", 1178 | ] 1179 | 1180 | [[package]] 1181 | name = "nom_locate" 1182 | version = "4.2.0" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" 1185 | dependencies = [ 1186 | "bytecount", 1187 | "memchr", 1188 | "nom", 1189 | ] 1190 | 1191 | [[package]] 1192 | name = "num_cpus" 1193 | version = "1.16.0" 1194 | source = "registry+https://github.com/rust-lang/crates.io-index" 1195 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 1196 | dependencies = [ 1197 | "hermit-abi", 1198 | "libc", 1199 | ] 1200 | 1201 | [[package]] 1202 | name = "object" 1203 | version = "0.32.2" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 1206 | dependencies = [ 1207 | "memchr", 1208 | ] 1209 | 1210 | [[package]] 1211 | name = "object" 1212 | version = "0.36.0" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" 1215 | dependencies = [ 1216 | "crc32fast", 1217 | "hashbrown 0.14.3", 1218 | "indexmap", 1219 | "memchr", 1220 | ] 1221 | 1222 | [[package]] 1223 | name = "once_cell" 1224 | version = "1.19.0" 1225 | source = "registry+https://github.com/rust-lang/crates.io-index" 1226 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 1227 | 1228 | [[package]] 1229 | name = "paste" 1230 | version = "1.0.14" 1231 | source = "registry+https://github.com/rust-lang/crates.io-index" 1232 | checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" 1233 | 1234 | [[package]] 1235 | name = "percent-encoding" 1236 | version = "2.3.1" 1237 | source = "registry+https://github.com/rust-lang/crates.io-index" 1238 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1239 | 1240 | [[package]] 1241 | name = "petgraph" 1242 | version = "0.6.4" 1243 | source = "registry+https://github.com/rust-lang/crates.io-index" 1244 | checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" 1245 | dependencies = [ 1246 | "fixedbitset", 1247 | "indexmap", 1248 | ] 1249 | 1250 | [[package]] 1251 | name = "pin-project-lite" 1252 | version = "0.2.13" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" 1255 | 1256 | [[package]] 1257 | name = "pin-utils" 1258 | version = "0.1.0" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1261 | 1262 | [[package]] 1263 | name = "pkg-config" 1264 | version = "0.3.28" 1265 | source = "registry+https://github.com/rust-lang/crates.io-index" 1266 | checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" 1267 | 1268 | [[package]] 1269 | name = "postcard" 1270 | version = "1.0.8" 1271 | source = "registry+https://github.com/rust-lang/crates.io-index" 1272 | checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" 1273 | dependencies = [ 1274 | "cobs", 1275 | "embedded-io", 1276 | "serde", 1277 | ] 1278 | 1279 | [[package]] 1280 | name = "ppv-lite86" 1281 | version = "0.2.17" 1282 | source = "registry+https://github.com/rust-lang/crates.io-index" 1283 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 1284 | 1285 | [[package]] 1286 | name = "proc-macro2" 1287 | version = "1.0.75" 1288 | source = "registry+https://github.com/rust-lang/crates.io-index" 1289 | checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" 1290 | dependencies = [ 1291 | "unicode-ident", 1292 | ] 1293 | 1294 | [[package]] 1295 | name = "psm" 1296 | version = "0.1.21" 1297 | source = "registry+https://github.com/rust-lang/crates.io-index" 1298 | checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" 1299 | dependencies = [ 1300 | "cc", 1301 | ] 1302 | 1303 | [[package]] 1304 | name = "quote" 1305 | version = "1.0.35" 1306 | source = "registry+https://github.com/rust-lang/crates.io-index" 1307 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 1308 | dependencies = [ 1309 | "proc-macro2", 1310 | ] 1311 | 1312 | [[package]] 1313 | name = "radix_trie" 1314 | version = "0.2.1" 1315 | source = "registry+https://github.com/rust-lang/crates.io-index" 1316 | checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" 1317 | dependencies = [ 1318 | "endian-type", 1319 | "nibble_vec", 1320 | ] 1321 | 1322 | [[package]] 1323 | name = "rand" 1324 | version = "0.8.5" 1325 | source = "registry+https://github.com/rust-lang/crates.io-index" 1326 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1327 | dependencies = [ 1328 | "libc", 1329 | "rand_chacha", 1330 | "rand_core", 1331 | ] 1332 | 1333 | [[package]] 1334 | name = "rand_chacha" 1335 | version = "0.3.1" 1336 | source = "registry+https://github.com/rust-lang/crates.io-index" 1337 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1338 | dependencies = [ 1339 | "ppv-lite86", 1340 | "rand_core", 1341 | ] 1342 | 1343 | [[package]] 1344 | name = "rand_core" 1345 | version = "0.6.4" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1348 | dependencies = [ 1349 | "getrandom", 1350 | ] 1351 | 1352 | [[package]] 1353 | name = "rand_xoshiro" 1354 | version = "0.6.0" 1355 | source = "registry+https://github.com/rust-lang/crates.io-index" 1356 | checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" 1357 | dependencies = [ 1358 | "rand_core", 1359 | ] 1360 | 1361 | [[package]] 1362 | name = "rayon" 1363 | version = "1.8.0" 1364 | source = "registry+https://github.com/rust-lang/crates.io-index" 1365 | checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" 1366 | dependencies = [ 1367 | "either", 1368 | "rayon-core", 1369 | ] 1370 | 1371 | [[package]] 1372 | name = "rayon-core" 1373 | version = "1.12.0" 1374 | source = "registry+https://github.com/rust-lang/crates.io-index" 1375 | checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" 1376 | dependencies = [ 1377 | "crossbeam-deque", 1378 | "crossbeam-utils", 1379 | ] 1380 | 1381 | [[package]] 1382 | name = "redox_syscall" 1383 | version = "0.4.1" 1384 | source = "registry+https://github.com/rust-lang/crates.io-index" 1385 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 1386 | dependencies = [ 1387 | "bitflags 1.3.2", 1388 | ] 1389 | 1390 | [[package]] 1391 | name = "redox_users" 1392 | version = "0.4.4" 1393 | source = "registry+https://github.com/rust-lang/crates.io-index" 1394 | checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" 1395 | dependencies = [ 1396 | "getrandom", 1397 | "libredox", 1398 | "thiserror", 1399 | ] 1400 | 1401 | [[package]] 1402 | name = "regalloc2" 1403 | version = "0.9.3" 1404 | source = "registry+https://github.com/rust-lang/crates.io-index" 1405 | checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" 1406 | dependencies = [ 1407 | "hashbrown 0.13.2", 1408 | "log", 1409 | "rustc-hash", 1410 | "slice-group-by", 1411 | "smallvec", 1412 | ] 1413 | 1414 | [[package]] 1415 | name = "regex" 1416 | version = "1.10.2" 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" 1418 | checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" 1419 | dependencies = [ 1420 | "aho-corasick", 1421 | "memchr", 1422 | "regex-automata", 1423 | "regex-syntax", 1424 | ] 1425 | 1426 | [[package]] 1427 | name = "regex-automata" 1428 | version = "0.4.3" 1429 | source = "registry+https://github.com/rust-lang/crates.io-index" 1430 | checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" 1431 | dependencies = [ 1432 | "aho-corasick", 1433 | "memchr", 1434 | "regex-syntax", 1435 | ] 1436 | 1437 | [[package]] 1438 | name = "regex-syntax" 1439 | version = "0.8.2" 1440 | source = "registry+https://github.com/rust-lang/crates.io-index" 1441 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 1442 | 1443 | [[package]] 1444 | name = "rustc-demangle" 1445 | version = "0.1.23" 1446 | source = "registry+https://github.com/rust-lang/crates.io-index" 1447 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 1448 | 1449 | [[package]] 1450 | name = "rustc-hash" 1451 | version = "1.1.0" 1452 | source = "registry+https://github.com/rust-lang/crates.io-index" 1453 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1454 | 1455 | [[package]] 1456 | name = "rustix" 1457 | version = "0.38.34" 1458 | source = "registry+https://github.com/rust-lang/crates.io-index" 1459 | checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" 1460 | dependencies = [ 1461 | "bitflags 2.4.1", 1462 | "errno", 1463 | "itoa", 1464 | "libc", 1465 | "linux-raw-sys", 1466 | "once_cell", 1467 | "windows-sys 0.52.0", 1468 | ] 1469 | 1470 | [[package]] 1471 | name = "rustyline" 1472 | version = "14.0.0" 1473 | source = "registry+https://github.com/rust-lang/crates.io-index" 1474 | checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63" 1475 | dependencies = [ 1476 | "bitflags 2.4.1", 1477 | "cfg-if", 1478 | "clipboard-win", 1479 | "fd-lock", 1480 | "home", 1481 | "libc", 1482 | "log", 1483 | "memchr", 1484 | "nix", 1485 | "radix_trie", 1486 | "unicode-segmentation", 1487 | "unicode-width", 1488 | "utf8parse", 1489 | "windows-sys 0.52.0", 1490 | ] 1491 | 1492 | [[package]] 1493 | name = "ryu" 1494 | version = "1.0.16" 1495 | source = "registry+https://github.com/rust-lang/crates.io-index" 1496 | checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" 1497 | 1498 | [[package]] 1499 | name = "semver" 1500 | version = "1.0.21" 1501 | source = "registry+https://github.com/rust-lang/crates.io-index" 1502 | checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" 1503 | 1504 | [[package]] 1505 | name = "serde" 1506 | version = "1.0.194" 1507 | source = "registry+https://github.com/rust-lang/crates.io-index" 1508 | checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" 1509 | dependencies = [ 1510 | "serde_derive", 1511 | ] 1512 | 1513 | [[package]] 1514 | name = "serde_derive" 1515 | version = "1.0.194" 1516 | source = "registry+https://github.com/rust-lang/crates.io-index" 1517 | checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" 1518 | dependencies = [ 1519 | "proc-macro2", 1520 | "quote", 1521 | "syn", 1522 | ] 1523 | 1524 | [[package]] 1525 | name = "serde_json" 1526 | version = "1.0.111" 1527 | source = "registry+https://github.com/rust-lang/crates.io-index" 1528 | checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" 1529 | dependencies = [ 1530 | "itoa", 1531 | "ryu", 1532 | "serde", 1533 | ] 1534 | 1535 | [[package]] 1536 | name = "serde_spanned" 1537 | version = "0.6.5" 1538 | source = "registry+https://github.com/rust-lang/crates.io-index" 1539 | checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" 1540 | dependencies = [ 1541 | "serde", 1542 | ] 1543 | 1544 | [[package]] 1545 | name = "serde_yaml" 1546 | version = "0.9.30" 1547 | source = "registry+https://github.com/rust-lang/crates.io-index" 1548 | checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38" 1549 | dependencies = [ 1550 | "indexmap", 1551 | "itoa", 1552 | "ryu", 1553 | "serde", 1554 | "unsafe-libyaml", 1555 | ] 1556 | 1557 | [[package]] 1558 | name = "sha2" 1559 | version = "0.10.8" 1560 | source = "registry+https://github.com/rust-lang/crates.io-index" 1561 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 1562 | dependencies = [ 1563 | "cfg-if", 1564 | "cpufeatures", 1565 | "digest", 1566 | ] 1567 | 1568 | [[package]] 1569 | name = "shellexpand" 1570 | version = "2.1.2" 1571 | source = "registry+https://github.com/rust-lang/crates.io-index" 1572 | checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" 1573 | dependencies = [ 1574 | "dirs", 1575 | ] 1576 | 1577 | [[package]] 1578 | name = "sized-chunks" 1579 | version = "0.6.5" 1580 | source = "registry+https://github.com/rust-lang/crates.io-index" 1581 | checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" 1582 | dependencies = [ 1583 | "bitmaps", 1584 | "typenum", 1585 | ] 1586 | 1587 | [[package]] 1588 | name = "slice-group-by" 1589 | version = "0.3.1" 1590 | source = "registry+https://github.com/rust-lang/crates.io-index" 1591 | checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" 1592 | 1593 | [[package]] 1594 | name = "smallvec" 1595 | version = "1.11.2" 1596 | source = "registry+https://github.com/rust-lang/crates.io-index" 1597 | checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" 1598 | dependencies = [ 1599 | "serde", 1600 | ] 1601 | 1602 | [[package]] 1603 | name = "socket2" 1604 | version = "0.5.5" 1605 | source = "registry+https://github.com/rust-lang/crates.io-index" 1606 | checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" 1607 | dependencies = [ 1608 | "libc", 1609 | "windows-sys 0.48.0", 1610 | ] 1611 | 1612 | [[package]] 1613 | name = "spdx" 1614 | version = "0.10.3" 1615 | source = "registry+https://github.com/rust-lang/crates.io-index" 1616 | checksum = "62bde1398b09b9f93fc2fc9b9da86e362693e999d3a54a8ac47a99a5a73f638b" 1617 | dependencies = [ 1618 | "smallvec", 1619 | ] 1620 | 1621 | [[package]] 1622 | name = "sptr" 1623 | version = "0.3.2" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" 1626 | 1627 | [[package]] 1628 | name = "stable_deref_trait" 1629 | version = "1.2.0" 1630 | source = "registry+https://github.com/rust-lang/crates.io-index" 1631 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1632 | 1633 | [[package]] 1634 | name = "strsim" 1635 | version = "0.11.0" 1636 | source = "registry+https://github.com/rust-lang/crates.io-index" 1637 | checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" 1638 | 1639 | [[package]] 1640 | name = "syn" 1641 | version = "2.0.48" 1642 | source = "registry+https://github.com/rust-lang/crates.io-index" 1643 | checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" 1644 | dependencies = [ 1645 | "proc-macro2", 1646 | "quote", 1647 | "unicode-ident", 1648 | ] 1649 | 1650 | [[package]] 1651 | name = "system-interface" 1652 | version = "0.27.2" 1653 | source = "registry+https://github.com/rust-lang/crates.io-index" 1654 | checksum = "b858526d22750088a9b3cf2e3c2aacebd5377f13adeec02860c30d09113010a6" 1655 | dependencies = [ 1656 | "bitflags 2.4.1", 1657 | "cap-fs-ext", 1658 | "cap-std", 1659 | "fd-lock", 1660 | "io-lifetimes", 1661 | "rustix", 1662 | "windows-sys 0.52.0", 1663 | "winx", 1664 | ] 1665 | 1666 | [[package]] 1667 | name = "target-lexicon" 1668 | version = "0.12.13" 1669 | source = "registry+https://github.com/rust-lang/crates.io-index" 1670 | checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" 1671 | 1672 | [[package]] 1673 | name = "thiserror" 1674 | version = "1.0.56" 1675 | source = "registry+https://github.com/rust-lang/crates.io-index" 1676 | checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" 1677 | dependencies = [ 1678 | "thiserror-impl", 1679 | ] 1680 | 1681 | [[package]] 1682 | name = "thiserror-impl" 1683 | version = "1.0.56" 1684 | source = "registry+https://github.com/rust-lang/crates.io-index" 1685 | checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" 1686 | dependencies = [ 1687 | "proc-macro2", 1688 | "quote", 1689 | "syn", 1690 | ] 1691 | 1692 | [[package]] 1693 | name = "tinyvec" 1694 | version = "1.6.0" 1695 | source = "registry+https://github.com/rust-lang/crates.io-index" 1696 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 1697 | dependencies = [ 1698 | "tinyvec_macros", 1699 | ] 1700 | 1701 | [[package]] 1702 | name = "tinyvec_macros" 1703 | version = "0.1.1" 1704 | source = "registry+https://github.com/rust-lang/crates.io-index" 1705 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1706 | 1707 | [[package]] 1708 | name = "tokio" 1709 | version = "1.38.0" 1710 | source = "registry+https://github.com/rust-lang/crates.io-index" 1711 | checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" 1712 | dependencies = [ 1713 | "backtrace", 1714 | "bytes", 1715 | "libc", 1716 | "mio", 1717 | "num_cpus", 1718 | "pin-project-lite", 1719 | "socket2", 1720 | "tokio-macros", 1721 | "windows-sys 0.48.0", 1722 | ] 1723 | 1724 | [[package]] 1725 | name = "tokio-macros" 1726 | version = "2.3.0" 1727 | source = "registry+https://github.com/rust-lang/crates.io-index" 1728 | checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" 1729 | dependencies = [ 1730 | "proc-macro2", 1731 | "quote", 1732 | "syn", 1733 | ] 1734 | 1735 | [[package]] 1736 | name = "toml" 1737 | version = "0.8.10" 1738 | source = "registry+https://github.com/rust-lang/crates.io-index" 1739 | checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" 1740 | dependencies = [ 1741 | "serde", 1742 | "serde_spanned", 1743 | "toml_datetime", 1744 | "toml_edit", 1745 | ] 1746 | 1747 | [[package]] 1748 | name = "toml_datetime" 1749 | version = "0.6.5" 1750 | source = "registry+https://github.com/rust-lang/crates.io-index" 1751 | checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" 1752 | dependencies = [ 1753 | "serde", 1754 | ] 1755 | 1756 | [[package]] 1757 | name = "toml_edit" 1758 | version = "0.22.5" 1759 | source = "registry+https://github.com/rust-lang/crates.io-index" 1760 | checksum = "99e68c159e8f5ba8a28c4eb7b0c0c190d77bb479047ca713270048145a9ad28a" 1761 | dependencies = [ 1762 | "indexmap", 1763 | "serde", 1764 | "serde_spanned", 1765 | "toml_datetime", 1766 | "winnow", 1767 | ] 1768 | 1769 | [[package]] 1770 | name = "tracing" 1771 | version = "0.1.40" 1772 | source = "registry+https://github.com/rust-lang/crates.io-index" 1773 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 1774 | dependencies = [ 1775 | "pin-project-lite", 1776 | "tracing-attributes", 1777 | "tracing-core", 1778 | ] 1779 | 1780 | [[package]] 1781 | name = "tracing-attributes" 1782 | version = "0.1.27" 1783 | source = "registry+https://github.com/rust-lang/crates.io-index" 1784 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 1785 | dependencies = [ 1786 | "proc-macro2", 1787 | "quote", 1788 | "syn", 1789 | ] 1790 | 1791 | [[package]] 1792 | name = "tracing-core" 1793 | version = "0.1.32" 1794 | source = "registry+https://github.com/rust-lang/crates.io-index" 1795 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 1796 | dependencies = [ 1797 | "once_cell", 1798 | ] 1799 | 1800 | [[package]] 1801 | name = "typenum" 1802 | version = "1.17.0" 1803 | source = "registry+https://github.com/rust-lang/crates.io-index" 1804 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 1805 | 1806 | [[package]] 1807 | name = "unicode-bidi" 1808 | version = "0.3.14" 1809 | source = "registry+https://github.com/rust-lang/crates.io-index" 1810 | checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" 1811 | 1812 | [[package]] 1813 | name = "unicode-ident" 1814 | version = "1.0.12" 1815 | source = "registry+https://github.com/rust-lang/crates.io-index" 1816 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1817 | 1818 | [[package]] 1819 | name = "unicode-normalization" 1820 | version = "0.1.22" 1821 | source = "registry+https://github.com/rust-lang/crates.io-index" 1822 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 1823 | dependencies = [ 1824 | "tinyvec", 1825 | ] 1826 | 1827 | [[package]] 1828 | name = "unicode-segmentation" 1829 | version = "1.10.1" 1830 | source = "registry+https://github.com/rust-lang/crates.io-index" 1831 | checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" 1832 | 1833 | [[package]] 1834 | name = "unicode-width" 1835 | version = "0.1.11" 1836 | source = "registry+https://github.com/rust-lang/crates.io-index" 1837 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 1838 | 1839 | [[package]] 1840 | name = "unicode-xid" 1841 | version = "0.2.4" 1842 | source = "registry+https://github.com/rust-lang/crates.io-index" 1843 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 1844 | 1845 | [[package]] 1846 | name = "unsafe-libyaml" 1847 | version = "0.2.10" 1848 | source = "registry+https://github.com/rust-lang/crates.io-index" 1849 | checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" 1850 | 1851 | [[package]] 1852 | name = "url" 1853 | version = "2.5.0" 1854 | source = "registry+https://github.com/rust-lang/crates.io-index" 1855 | checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" 1856 | dependencies = [ 1857 | "form_urlencoded", 1858 | "idna", 1859 | "percent-encoding", 1860 | ] 1861 | 1862 | [[package]] 1863 | name = "utf8parse" 1864 | version = "0.2.1" 1865 | source = "registry+https://github.com/rust-lang/crates.io-index" 1866 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 1867 | 1868 | [[package]] 1869 | name = "uuid" 1870 | version = "1.6.1" 1871 | source = "registry+https://github.com/rust-lang/crates.io-index" 1872 | checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" 1873 | 1874 | [[package]] 1875 | name = "version_check" 1876 | version = "0.9.4" 1877 | source = "registry+https://github.com/rust-lang/crates.io-index" 1878 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1879 | 1880 | [[package]] 1881 | name = "wasi" 1882 | version = "0.11.0+wasi-snapshot-preview1" 1883 | source = "registry+https://github.com/rust-lang/crates.io-index" 1884 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1885 | 1886 | [[package]] 1887 | name = "wasm-bindgen" 1888 | version = "0.2.89" 1889 | source = "registry+https://github.com/rust-lang/crates.io-index" 1890 | checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" 1891 | dependencies = [ 1892 | "cfg-if", 1893 | "wasm-bindgen-macro", 1894 | ] 1895 | 1896 | [[package]] 1897 | name = "wasm-bindgen-backend" 1898 | version = "0.2.89" 1899 | source = "registry+https://github.com/rust-lang/crates.io-index" 1900 | checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" 1901 | dependencies = [ 1902 | "bumpalo", 1903 | "log", 1904 | "once_cell", 1905 | "proc-macro2", 1906 | "quote", 1907 | "syn", 1908 | "wasm-bindgen-shared", 1909 | ] 1910 | 1911 | [[package]] 1912 | name = "wasm-bindgen-macro" 1913 | version = "0.2.89" 1914 | source = "registry+https://github.com/rust-lang/crates.io-index" 1915 | checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" 1916 | dependencies = [ 1917 | "quote", 1918 | "wasm-bindgen-macro-support", 1919 | ] 1920 | 1921 | [[package]] 1922 | name = "wasm-bindgen-macro-support" 1923 | version = "0.2.89" 1924 | source = "registry+https://github.com/rust-lang/crates.io-index" 1925 | checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" 1926 | dependencies = [ 1927 | "proc-macro2", 1928 | "quote", 1929 | "syn", 1930 | "wasm-bindgen-backend", 1931 | "wasm-bindgen-shared", 1932 | ] 1933 | 1934 | [[package]] 1935 | name = "wasm-bindgen-shared" 1936 | version = "0.2.89" 1937 | source = "registry+https://github.com/rust-lang/crates.io-index" 1938 | checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" 1939 | 1940 | [[package]] 1941 | name = "wasm-compose" 1942 | version = "0.211.1" 1943 | source = "registry+https://github.com/rust-lang/crates.io-index" 1944 | checksum = "d42de0e10832f85403b6f0dedd42ba31ad5c819b68194e811caa1af38037ee45" 1945 | dependencies = [ 1946 | "anyhow", 1947 | "heck", 1948 | "im-rc", 1949 | "indexmap", 1950 | "log", 1951 | "petgraph", 1952 | "serde", 1953 | "serde_derive", 1954 | "serde_yaml", 1955 | "smallvec", 1956 | "wasm-encoder 0.211.1", 1957 | "wasmparser 0.211.1", 1958 | "wat", 1959 | ] 1960 | 1961 | [[package]] 1962 | name = "wasm-encoder" 1963 | version = "0.209.1" 1964 | source = "registry+https://github.com/rust-lang/crates.io-index" 1965 | checksum = "7b4a05336882dae732ce6bd48b7e11fe597293cb72c13da4f35d7d5f8d53b2a7" 1966 | dependencies = [ 1967 | "leb128", 1968 | ] 1969 | 1970 | [[package]] 1971 | name = "wasm-encoder" 1972 | version = "0.211.1" 1973 | source = "registry+https://github.com/rust-lang/crates.io-index" 1974 | checksum = "5e7d931a1120ef357f32b74547646b6fa68ea25e377772b72874b131a9ed70d4" 1975 | dependencies = [ 1976 | "leb128", 1977 | "wasmparser 0.211.1", 1978 | ] 1979 | 1980 | [[package]] 1981 | name = "wasm-metadata" 1982 | version = "0.211.1" 1983 | source = "registry+https://github.com/rust-lang/crates.io-index" 1984 | checksum = "7f56bad1c68558c44e7f60be865117dc9d8c3a066bcf3b2232cb9a9858965fd5" 1985 | dependencies = [ 1986 | "anyhow", 1987 | "indexmap", 1988 | "serde", 1989 | "serde_derive", 1990 | "serde_json", 1991 | "spdx", 1992 | "wasm-encoder 0.211.1", 1993 | "wasmparser 0.211.1", 1994 | ] 1995 | 1996 | [[package]] 1997 | name = "wasmparser" 1998 | version = "0.209.1" 1999 | source = "registry+https://github.com/rust-lang/crates.io-index" 2000 | checksum = "07035cc9a9b41e62d3bb3a3815a66ab87c993c06fe1cf6b2a3f2a18499d937db" 2001 | dependencies = [ 2002 | "ahash", 2003 | "bitflags 2.4.1", 2004 | "hashbrown 0.14.3", 2005 | "indexmap", 2006 | "semver", 2007 | "serde", 2008 | ] 2009 | 2010 | [[package]] 2011 | name = "wasmparser" 2012 | version = "0.211.1" 2013 | source = "registry+https://github.com/rust-lang/crates.io-index" 2014 | checksum = "3189cc8a91f547390e2f043ca3b3e3fe0892f7d581767fd4e4b7f3dc3fe8e561" 2015 | dependencies = [ 2016 | "ahash", 2017 | "bitflags 2.4.1", 2018 | "hashbrown 0.14.3", 2019 | "indexmap", 2020 | "semver", 2021 | ] 2022 | 2023 | [[package]] 2024 | name = "wasmprinter" 2025 | version = "0.209.1" 2026 | source = "registry+https://github.com/rust-lang/crates.io-index" 2027 | checksum = "ceca8ae6eaa8c7c87b33c25c53bdf299f8c2a764aee1179402ff7652ef3a6859" 2028 | dependencies = [ 2029 | "anyhow", 2030 | "wasmparser 0.209.1", 2031 | ] 2032 | 2033 | [[package]] 2034 | name = "wasmtime" 2035 | version = "22.0.0" 2036 | source = "registry+https://github.com/rust-lang/crates.io-index" 2037 | checksum = "786d8b5e7a4d54917c5ebe555b9667337e5f93383f49bddaaeec2eba68093b45" 2038 | dependencies = [ 2039 | "addr2line", 2040 | "anyhow", 2041 | "async-trait", 2042 | "bumpalo", 2043 | "cc", 2044 | "cfg-if", 2045 | "encoding_rs", 2046 | "fxprof-processed-profile", 2047 | "gimli", 2048 | "hashbrown 0.14.3", 2049 | "indexmap", 2050 | "ittapi", 2051 | "libc", 2052 | "libm", 2053 | "log", 2054 | "mach2", 2055 | "memfd", 2056 | "memoffset", 2057 | "object 0.36.0", 2058 | "once_cell", 2059 | "paste", 2060 | "postcard", 2061 | "psm", 2062 | "rayon", 2063 | "rustix", 2064 | "semver", 2065 | "serde", 2066 | "serde_derive", 2067 | "serde_json", 2068 | "smallvec", 2069 | "sptr", 2070 | "target-lexicon", 2071 | "wasm-encoder 0.209.1", 2072 | "wasmparser 0.209.1", 2073 | "wasmtime-asm-macros", 2074 | "wasmtime-cache", 2075 | "wasmtime-component-macro", 2076 | "wasmtime-component-util", 2077 | "wasmtime-cranelift", 2078 | "wasmtime-environ", 2079 | "wasmtime-fiber", 2080 | "wasmtime-jit-debug", 2081 | "wasmtime-jit-icache-coherence", 2082 | "wasmtime-slab", 2083 | "wasmtime-versioned-export-macros", 2084 | "wasmtime-winch", 2085 | "wat", 2086 | "windows-sys 0.52.0", 2087 | ] 2088 | 2089 | [[package]] 2090 | name = "wasmtime-asm-macros" 2091 | version = "22.0.0" 2092 | source = "registry+https://github.com/rust-lang/crates.io-index" 2093 | checksum = "d697d99c341d4a9ffb72f3af7a02124d233eeb59aee010f36d88e97cca553d5e" 2094 | dependencies = [ 2095 | "cfg-if", 2096 | ] 2097 | 2098 | [[package]] 2099 | name = "wasmtime-cache" 2100 | version = "22.0.0" 2101 | source = "registry+https://github.com/rust-lang/crates.io-index" 2102 | checksum = "916610f9ae9a6c22deb25bba2e6247ba9f00b093d30620875203b91328a1adfa" 2103 | dependencies = [ 2104 | "anyhow", 2105 | "base64", 2106 | "directories-next", 2107 | "log", 2108 | "postcard", 2109 | "rustix", 2110 | "serde", 2111 | "serde_derive", 2112 | "sha2", 2113 | "toml", 2114 | "windows-sys 0.52.0", 2115 | "zstd", 2116 | ] 2117 | 2118 | [[package]] 2119 | name = "wasmtime-component-macro" 2120 | version = "22.0.0" 2121 | source = "registry+https://github.com/rust-lang/crates.io-index" 2122 | checksum = "b29b462b068e73b5b27fae092a27f47e5937cabf6b26be2779c978698a52feca" 2123 | dependencies = [ 2124 | "anyhow", 2125 | "proc-macro2", 2126 | "quote", 2127 | "syn", 2128 | "wasmtime-component-util", 2129 | "wasmtime-wit-bindgen", 2130 | "wit-parser 0.209.1", 2131 | ] 2132 | 2133 | [[package]] 2134 | name = "wasmtime-component-util" 2135 | version = "22.0.0" 2136 | source = "registry+https://github.com/rust-lang/crates.io-index" 2137 | checksum = "f9d2912c53d9054984b380dfbd7579f9c3681b2a73b903a56bd71a1c4f175f1e" 2138 | 2139 | [[package]] 2140 | name = "wasmtime-cranelift" 2141 | version = "22.0.0" 2142 | source = "registry+https://github.com/rust-lang/crates.io-index" 2143 | checksum = "a3975deafea000457ba84355c7c0fce0372937204f77026510b7b454f28a3a65" 2144 | dependencies = [ 2145 | "anyhow", 2146 | "cfg-if", 2147 | "cranelift-codegen", 2148 | "cranelift-control", 2149 | "cranelift-entity", 2150 | "cranelift-frontend", 2151 | "cranelift-native", 2152 | "cranelift-wasm", 2153 | "gimli", 2154 | "log", 2155 | "object 0.36.0", 2156 | "target-lexicon", 2157 | "thiserror", 2158 | "wasmparser 0.209.1", 2159 | "wasmtime-environ", 2160 | "wasmtime-versioned-export-macros", 2161 | ] 2162 | 2163 | [[package]] 2164 | name = "wasmtime-environ" 2165 | version = "22.0.0" 2166 | source = "registry+https://github.com/rust-lang/crates.io-index" 2167 | checksum = "f444e900e848b884d8a8a2949b6f5b92af642a3e663ff8fbe78731143a55be61" 2168 | dependencies = [ 2169 | "anyhow", 2170 | "cpp_demangle", 2171 | "cranelift-entity", 2172 | "gimli", 2173 | "indexmap", 2174 | "log", 2175 | "object 0.36.0", 2176 | "postcard", 2177 | "rustc-demangle", 2178 | "serde", 2179 | "serde_derive", 2180 | "target-lexicon", 2181 | "wasm-encoder 0.209.1", 2182 | "wasmparser 0.209.1", 2183 | "wasmprinter", 2184 | "wasmtime-component-util", 2185 | "wasmtime-types", 2186 | ] 2187 | 2188 | [[package]] 2189 | name = "wasmtime-fiber" 2190 | version = "22.0.0" 2191 | source = "registry+https://github.com/rust-lang/crates.io-index" 2192 | checksum = "4ded58eb2d1bf0dcd2182d0ccd7055c4b10b50d711514f1d73f61515d0fa829d" 2193 | dependencies = [ 2194 | "anyhow", 2195 | "cc", 2196 | "cfg-if", 2197 | "rustix", 2198 | "wasmtime-asm-macros", 2199 | "wasmtime-versioned-export-macros", 2200 | "windows-sys 0.52.0", 2201 | ] 2202 | 2203 | [[package]] 2204 | name = "wasmtime-jit-debug" 2205 | version = "22.0.0" 2206 | source = "registry+https://github.com/rust-lang/crates.io-index" 2207 | checksum = "9bc54198c6720f098210a85efb3ba8c078d1de4d373cdb6778850a66ae088d11" 2208 | dependencies = [ 2209 | "object 0.36.0", 2210 | "once_cell", 2211 | "rustix", 2212 | "wasmtime-versioned-export-macros", 2213 | ] 2214 | 2215 | [[package]] 2216 | name = "wasmtime-jit-icache-coherence" 2217 | version = "22.0.0" 2218 | source = "registry+https://github.com/rust-lang/crates.io-index" 2219 | checksum = "5afe2f0499542f9a4bcfa1b55bfdda803b6ade4e7c93c6b99e0f39dba44b0a91" 2220 | dependencies = [ 2221 | "anyhow", 2222 | "cfg-if", 2223 | "libc", 2224 | "windows-sys 0.52.0", 2225 | ] 2226 | 2227 | [[package]] 2228 | name = "wasmtime-slab" 2229 | version = "22.0.0" 2230 | source = "registry+https://github.com/rust-lang/crates.io-index" 2231 | checksum = "0a7de1f2bec5bbb35d532e61c85c049dc84ae671df60492f90b954ecf21169e7" 2232 | 2233 | [[package]] 2234 | name = "wasmtime-types" 2235 | version = "22.0.0" 2236 | source = "registry+https://github.com/rust-lang/crates.io-index" 2237 | checksum = "412463e9000e14cf6856be48628d2213c20c153e29ffc22b036980c892ea6964" 2238 | dependencies = [ 2239 | "cranelift-entity", 2240 | "serde", 2241 | "serde_derive", 2242 | "smallvec", 2243 | "wasmparser 0.209.1", 2244 | ] 2245 | 2246 | [[package]] 2247 | name = "wasmtime-versioned-export-macros" 2248 | version = "22.0.0" 2249 | source = "registry+https://github.com/rust-lang/crates.io-index" 2250 | checksum = "de5a9bc4f44ceeb168e9e8e3be4e0b4beb9095b468479663a9e24c667e36826f" 2251 | dependencies = [ 2252 | "proc-macro2", 2253 | "quote", 2254 | "syn", 2255 | ] 2256 | 2257 | [[package]] 2258 | name = "wasmtime-wasi" 2259 | version = "22.0.0" 2260 | source = "registry+https://github.com/rust-lang/crates.io-index" 2261 | checksum = "8abb1301089ed8e0b4840f539cba316a73ac382090f1b25d22d8c8eed8df49c7" 2262 | dependencies = [ 2263 | "anyhow", 2264 | "async-trait", 2265 | "bitflags 2.4.1", 2266 | "bytes", 2267 | "cap-fs-ext", 2268 | "cap-net-ext", 2269 | "cap-rand", 2270 | "cap-std", 2271 | "cap-time-ext", 2272 | "fs-set-times", 2273 | "futures", 2274 | "io-extras", 2275 | "io-lifetimes", 2276 | "once_cell", 2277 | "rustix", 2278 | "system-interface", 2279 | "thiserror", 2280 | "tokio", 2281 | "tracing", 2282 | "url", 2283 | "wasmtime", 2284 | "wiggle", 2285 | "windows-sys 0.52.0", 2286 | ] 2287 | 2288 | [[package]] 2289 | name = "wasmtime-winch" 2290 | version = "22.0.0" 2291 | source = "registry+https://github.com/rust-lang/crates.io-index" 2292 | checksum = "ed4db238a0241df2d15f79ad17b3a37a27f2ea6cb885894d81b42ae107544466" 2293 | dependencies = [ 2294 | "anyhow", 2295 | "cranelift-codegen", 2296 | "gimli", 2297 | "object 0.36.0", 2298 | "target-lexicon", 2299 | "wasmparser 0.209.1", 2300 | "wasmtime-cranelift", 2301 | "wasmtime-environ", 2302 | "winch-codegen", 2303 | ] 2304 | 2305 | [[package]] 2306 | name = "wasmtime-wit-bindgen" 2307 | version = "22.0.0" 2308 | source = "registry+https://github.com/rust-lang/crates.io-index" 2309 | checksum = "70dc077306b38288262e5ba01d4b21532a6987416cdc0aedf04bb06c22a68fdc" 2310 | dependencies = [ 2311 | "anyhow", 2312 | "heck", 2313 | "indexmap", 2314 | "wit-parser 0.209.1", 2315 | ] 2316 | 2317 | [[package]] 2318 | name = "wast" 2319 | version = "35.0.2" 2320 | source = "registry+https://github.com/rust-lang/crates.io-index" 2321 | checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" 2322 | dependencies = [ 2323 | "leb128", 2324 | ] 2325 | 2326 | [[package]] 2327 | name = "wast" 2328 | version = "211.0.1" 2329 | source = "registry+https://github.com/rust-lang/crates.io-index" 2330 | checksum = "b25506dd82d00da6b14a87436b3d52b1d264083fa79cdb72a0d1b04a8595ccaa" 2331 | dependencies = [ 2332 | "bumpalo", 2333 | "leb128", 2334 | "memchr", 2335 | "unicode-width", 2336 | "wasm-encoder 0.211.1", 2337 | ] 2338 | 2339 | [[package]] 2340 | name = "wat" 2341 | version = "1.211.1" 2342 | source = "registry+https://github.com/rust-lang/crates.io-index" 2343 | checksum = "eb716ca6c86eecac2d82541ffc39860118fc0af9309c4f2670637bea2e1bdd7d" 2344 | dependencies = [ 2345 | "wast 211.0.1", 2346 | ] 2347 | 2348 | [[package]] 2349 | name = "wepl" 2350 | version = "0.1.0" 2351 | dependencies = [ 2352 | "anyhow", 2353 | "async-trait", 2354 | "bytes", 2355 | "clap", 2356 | "colored", 2357 | "env_logger", 2358 | "home", 2359 | "log", 2360 | "nom", 2361 | "nom_locate", 2362 | "rustyline", 2363 | "tokio", 2364 | "wasm-compose", 2365 | "wasmtime", 2366 | "wasmtime-wasi", 2367 | "wit-component", 2368 | "wit-parser 0.211.1", 2369 | ] 2370 | 2371 | [[package]] 2372 | name = "wiggle" 2373 | version = "22.0.0" 2374 | source = "registry+https://github.com/rust-lang/crates.io-index" 2375 | checksum = "29830e5d01c182d24b94092c697aa7ab0ee97d22e78a2bf40ca91eae6ebca5c2" 2376 | dependencies = [ 2377 | "anyhow", 2378 | "async-trait", 2379 | "bitflags 2.4.1", 2380 | "thiserror", 2381 | "tracing", 2382 | "wasmtime", 2383 | "wiggle-macro", 2384 | ] 2385 | 2386 | [[package]] 2387 | name = "wiggle-generate" 2388 | version = "22.0.0" 2389 | source = "registry+https://github.com/rust-lang/crates.io-index" 2390 | checksum = "557567f2793508760cd855f7659b7a0b9dc4dbc451f53f1415d6943a15311ade" 2391 | dependencies = [ 2392 | "anyhow", 2393 | "heck", 2394 | "proc-macro2", 2395 | "quote", 2396 | "shellexpand", 2397 | "syn", 2398 | "witx", 2399 | ] 2400 | 2401 | [[package]] 2402 | name = "wiggle-macro" 2403 | version = "22.0.0" 2404 | source = "registry+https://github.com/rust-lang/crates.io-index" 2405 | checksum = "cc26129a8aea20b62c961d1b9ab4a3c3b56b10042ed85d004f8678af0f21ba6e" 2406 | dependencies = [ 2407 | "proc-macro2", 2408 | "quote", 2409 | "syn", 2410 | "wiggle-generate", 2411 | ] 2412 | 2413 | [[package]] 2414 | name = "winapi" 2415 | version = "0.3.9" 2416 | source = "registry+https://github.com/rust-lang/crates.io-index" 2417 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2418 | dependencies = [ 2419 | "winapi-i686-pc-windows-gnu", 2420 | "winapi-x86_64-pc-windows-gnu", 2421 | ] 2422 | 2423 | [[package]] 2424 | name = "winapi-i686-pc-windows-gnu" 2425 | version = "0.4.0" 2426 | source = "registry+https://github.com/rust-lang/crates.io-index" 2427 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2428 | 2429 | [[package]] 2430 | name = "winapi-x86_64-pc-windows-gnu" 2431 | version = "0.4.0" 2432 | source = "registry+https://github.com/rust-lang/crates.io-index" 2433 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2434 | 2435 | [[package]] 2436 | name = "winch-codegen" 2437 | version = "0.20.0" 2438 | source = "registry+https://github.com/rust-lang/crates.io-index" 2439 | checksum = "85c6915884e731b2db0d8cf08cb64474cb69221a161675fd3c135f91febc3daa" 2440 | dependencies = [ 2441 | "anyhow", 2442 | "cranelift-codegen", 2443 | "gimli", 2444 | "regalloc2", 2445 | "smallvec", 2446 | "target-lexicon", 2447 | "wasmparser 0.209.1", 2448 | "wasmtime-cranelift", 2449 | "wasmtime-environ", 2450 | ] 2451 | 2452 | [[package]] 2453 | name = "windows-core" 2454 | version = "0.52.0" 2455 | source = "registry+https://github.com/rust-lang/crates.io-index" 2456 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 2457 | dependencies = [ 2458 | "windows-targets 0.52.0", 2459 | ] 2460 | 2461 | [[package]] 2462 | name = "windows-sys" 2463 | version = "0.48.0" 2464 | source = "registry+https://github.com/rust-lang/crates.io-index" 2465 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 2466 | dependencies = [ 2467 | "windows-targets 0.48.5", 2468 | ] 2469 | 2470 | [[package]] 2471 | name = "windows-sys" 2472 | version = "0.52.0" 2473 | source = "registry+https://github.com/rust-lang/crates.io-index" 2474 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2475 | dependencies = [ 2476 | "windows-targets 0.52.0", 2477 | ] 2478 | 2479 | [[package]] 2480 | name = "windows-targets" 2481 | version = "0.48.5" 2482 | source = "registry+https://github.com/rust-lang/crates.io-index" 2483 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2484 | dependencies = [ 2485 | "windows_aarch64_gnullvm 0.48.5", 2486 | "windows_aarch64_msvc 0.48.5", 2487 | "windows_i686_gnu 0.48.5", 2488 | "windows_i686_msvc 0.48.5", 2489 | "windows_x86_64_gnu 0.48.5", 2490 | "windows_x86_64_gnullvm 0.48.5", 2491 | "windows_x86_64_msvc 0.48.5", 2492 | ] 2493 | 2494 | [[package]] 2495 | name = "windows-targets" 2496 | version = "0.52.0" 2497 | source = "registry+https://github.com/rust-lang/crates.io-index" 2498 | checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" 2499 | dependencies = [ 2500 | "windows_aarch64_gnullvm 0.52.0", 2501 | "windows_aarch64_msvc 0.52.0", 2502 | "windows_i686_gnu 0.52.0", 2503 | "windows_i686_msvc 0.52.0", 2504 | "windows_x86_64_gnu 0.52.0", 2505 | "windows_x86_64_gnullvm 0.52.0", 2506 | "windows_x86_64_msvc 0.52.0", 2507 | ] 2508 | 2509 | [[package]] 2510 | name = "windows_aarch64_gnullvm" 2511 | version = "0.48.5" 2512 | source = "registry+https://github.com/rust-lang/crates.io-index" 2513 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2514 | 2515 | [[package]] 2516 | name = "windows_aarch64_gnullvm" 2517 | version = "0.52.0" 2518 | source = "registry+https://github.com/rust-lang/crates.io-index" 2519 | checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" 2520 | 2521 | [[package]] 2522 | name = "windows_aarch64_msvc" 2523 | version = "0.48.5" 2524 | source = "registry+https://github.com/rust-lang/crates.io-index" 2525 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2526 | 2527 | [[package]] 2528 | name = "windows_aarch64_msvc" 2529 | version = "0.52.0" 2530 | source = "registry+https://github.com/rust-lang/crates.io-index" 2531 | checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" 2532 | 2533 | [[package]] 2534 | name = "windows_i686_gnu" 2535 | version = "0.48.5" 2536 | source = "registry+https://github.com/rust-lang/crates.io-index" 2537 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2538 | 2539 | [[package]] 2540 | name = "windows_i686_gnu" 2541 | version = "0.52.0" 2542 | source = "registry+https://github.com/rust-lang/crates.io-index" 2543 | checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" 2544 | 2545 | [[package]] 2546 | name = "windows_i686_msvc" 2547 | version = "0.48.5" 2548 | source = "registry+https://github.com/rust-lang/crates.io-index" 2549 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2550 | 2551 | [[package]] 2552 | name = "windows_i686_msvc" 2553 | version = "0.52.0" 2554 | source = "registry+https://github.com/rust-lang/crates.io-index" 2555 | checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" 2556 | 2557 | [[package]] 2558 | name = "windows_x86_64_gnu" 2559 | version = "0.48.5" 2560 | source = "registry+https://github.com/rust-lang/crates.io-index" 2561 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2562 | 2563 | [[package]] 2564 | name = "windows_x86_64_gnu" 2565 | version = "0.52.0" 2566 | source = "registry+https://github.com/rust-lang/crates.io-index" 2567 | checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" 2568 | 2569 | [[package]] 2570 | name = "windows_x86_64_gnullvm" 2571 | version = "0.48.5" 2572 | source = "registry+https://github.com/rust-lang/crates.io-index" 2573 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2574 | 2575 | [[package]] 2576 | name = "windows_x86_64_gnullvm" 2577 | version = "0.52.0" 2578 | source = "registry+https://github.com/rust-lang/crates.io-index" 2579 | checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" 2580 | 2581 | [[package]] 2582 | name = "windows_x86_64_msvc" 2583 | version = "0.48.5" 2584 | source = "registry+https://github.com/rust-lang/crates.io-index" 2585 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2586 | 2587 | [[package]] 2588 | name = "windows_x86_64_msvc" 2589 | version = "0.52.0" 2590 | source = "registry+https://github.com/rust-lang/crates.io-index" 2591 | checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" 2592 | 2593 | [[package]] 2594 | name = "winnow" 2595 | version = "0.6.0" 2596 | source = "registry+https://github.com/rust-lang/crates.io-index" 2597 | checksum = "6b1dbce9e90e5404c5a52ed82b1d13fc8cfbdad85033b6f57546ffd1265f8451" 2598 | dependencies = [ 2599 | "memchr", 2600 | ] 2601 | 2602 | [[package]] 2603 | name = "winx" 2604 | version = "0.36.3" 2605 | source = "registry+https://github.com/rust-lang/crates.io-index" 2606 | checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" 2607 | dependencies = [ 2608 | "bitflags 2.4.1", 2609 | "windows-sys 0.52.0", 2610 | ] 2611 | 2612 | [[package]] 2613 | name = "wit-component" 2614 | version = "0.211.1" 2615 | source = "registry+https://github.com/rust-lang/crates.io-index" 2616 | checksum = "079a38b7d679867424bf2bcbdd553a2acf364525307e43dfb910fa4a2c6fd9f2" 2617 | dependencies = [ 2618 | "anyhow", 2619 | "bitflags 2.4.1", 2620 | "indexmap", 2621 | "log", 2622 | "serde", 2623 | "serde_derive", 2624 | "serde_json", 2625 | "wasm-encoder 0.211.1", 2626 | "wasm-metadata", 2627 | "wasmparser 0.211.1", 2628 | "wit-parser 0.211.1", 2629 | ] 2630 | 2631 | [[package]] 2632 | name = "wit-parser" 2633 | version = "0.209.1" 2634 | source = "registry+https://github.com/rust-lang/crates.io-index" 2635 | checksum = "3e79b9e3c0b6bb589dec46317e645851e0db2734c44e2be5e251b03ff4a51269" 2636 | dependencies = [ 2637 | "anyhow", 2638 | "id-arena", 2639 | "indexmap", 2640 | "log", 2641 | "semver", 2642 | "serde", 2643 | "serde_derive", 2644 | "serde_json", 2645 | "unicode-xid", 2646 | "wasmparser 0.209.1", 2647 | ] 2648 | 2649 | [[package]] 2650 | name = "wit-parser" 2651 | version = "0.211.1" 2652 | source = "registry+https://github.com/rust-lang/crates.io-index" 2653 | checksum = "a3cc90c50c7ec8a824b5d2cddddff13b2dc12b7a96bf8684d11474223c2ea22f" 2654 | dependencies = [ 2655 | "anyhow", 2656 | "id-arena", 2657 | "indexmap", 2658 | "log", 2659 | "semver", 2660 | "serde", 2661 | "serde_derive", 2662 | "serde_json", 2663 | "unicode-xid", 2664 | "wasmparser 0.211.1", 2665 | ] 2666 | 2667 | [[package]] 2668 | name = "witx" 2669 | version = "0.9.1" 2670 | source = "registry+https://github.com/rust-lang/crates.io-index" 2671 | checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" 2672 | dependencies = [ 2673 | "anyhow", 2674 | "log", 2675 | "thiserror", 2676 | "wast 35.0.2", 2677 | ] 2678 | 2679 | [[package]] 2680 | name = "zerocopy" 2681 | version = "0.7.32" 2682 | source = "registry+https://github.com/rust-lang/crates.io-index" 2683 | checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" 2684 | dependencies = [ 2685 | "zerocopy-derive", 2686 | ] 2687 | 2688 | [[package]] 2689 | name = "zerocopy-derive" 2690 | version = "0.7.32" 2691 | source = "registry+https://github.com/rust-lang/crates.io-index" 2692 | checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" 2693 | dependencies = [ 2694 | "proc-macro2", 2695 | "quote", 2696 | "syn", 2697 | ] 2698 | 2699 | [[package]] 2700 | name = "zstd" 2701 | version = "0.13.1" 2702 | source = "registry+https://github.com/rust-lang/crates.io-index" 2703 | checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" 2704 | dependencies = [ 2705 | "zstd-safe", 2706 | ] 2707 | 2708 | [[package]] 2709 | name = "zstd-safe" 2710 | version = "7.1.0" 2711 | source = "registry+https://github.com/rust-lang/crates.io-index" 2712 | checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" 2713 | dependencies = [ 2714 | "zstd-sys", 2715 | ] 2716 | 2717 | [[package]] 2718 | name = "zstd-sys" 2719 | version = "2.0.11+zstd.1.5.6" 2720 | source = "registry+https://github.com/rust-lang/crates.io-index" 2721 | checksum = "75652c55c0b6f3e6f12eb786fe1bc960396bf05a1eb3bf1f3691c3610ac2e6d4" 2722 | dependencies = [ 2723 | "cc", 2724 | "pkg-config", 2725 | ] 2726 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wepl" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "The WebAssembly Component repl." 6 | license = "Apache-2.0 WITH LLVM-exception" 7 | categories = ["wasm"] 8 | keywords = ["webassembly", "wasm"] 9 | repository = "https://github.com/rylev/wepl" 10 | readme = "README.md" 11 | 12 | [dependencies] 13 | anyhow = "1.0" 14 | async-trait = "0.1" 15 | bytes = "1.6" 16 | clap = { version = "4.5", features = ["derive"] } 17 | colored = "2.1" 18 | env_logger = "0.11" 19 | home = "0.5" 20 | log = "0.4" 21 | nom = "7.1" 22 | nom_locate = "4.2" 23 | rustyline = "14.0" 24 | tokio = { version = "1.38", features = ["macros"] } 25 | wasmtime = "22.0" 26 | wasmtime-wasi = "22.0" 27 | wit-component = "0.211" 28 | wit-parser = "0.211" 29 | wasm-compose = "0.211" 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | 205 | --- LLVM Exceptions to the Apache 2.0 License ---- 206 | 207 | As an exception, if, as a result of your compiling your source code, portions 208 | of this Software are embedded into an Object form of such source code, you 209 | may redistribute such embedded portions in such Object form without complying 210 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 211 | 212 | In addition, if you combine or link compiled forms of this Software with 213 | software that is licensed under the GPLv2 ("Combined Software") and if a 214 | court of competent jurisdiction determines that the patent provision (Section 215 | 3), the indemnity provision (Section 9) or other Section of the License 216 | conflicts with the conditions of the GPLv2, you may retroactively and 217 | prospectively choose to deem waived or otherwise exclude such Section(s) of 218 | the License, but only in their entirety and only with respect to the Combined 219 | Software. 220 | 221 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WEPL 2 | 3 | The WebAssembly Component repl. 4 | 5 | ## Building from Source 6 | 7 | You can build `wepl` from source by running `cargo build --release` (or `cargo build` to build in debug mode). 8 | 9 | ## Installing `wepl` 10 | 11 | You can install `wepl` from crates.io by running `cargo install wepl --locked`. 12 | 13 | You can also install `wepl` on your local system by running `cargo install --path . --locked`. 14 | 15 | ## Example 16 | 17 | ```bash 18 | $ wepl mycomponent.wasm 19 | > .exports 20 | uppercase: func(input: string) -> string 21 | > uppercase 22 | uppercase: func(input: string) -> string 23 | > s = "hello" 24 | s: string 25 | > uppercase(s) 26 | "HELLO" 27 | ``` 28 | 29 | ## Built-in Functions 30 | 31 | Built-in functions can be called by using the `.` prefix. 32 | 33 | Supported functions include: 34 | * `.imports`: print a list of all the component's imports 35 | * `.exports`: print a list of all the component's exports 36 | * `.link $function $wasm`: satisfy the imported function `$func` with an export from the wasm component `$wasm` 37 | * `.compose $adapter`: satisfy imports with the supplied adapter module (e.g., to compose with [`WASI-Virt`](https://github.com/bytecodealliance/WASI-Virt) adapter) 38 | * `.type $type`: inspect a type's `$type` definition in scope 39 | * `.help`: print help information (`?` is alias for this built-in) 40 | 41 | ## Features 42 | 43 | * Exported function evaluation 44 | * Listing imports and exports 45 | * Variable assignment 46 | * Type checking 47 | * Satisfying imports with other WebAssembly Components 48 | * Basic component composition 49 | 50 | ## Compatibility 51 | 52 | `wepl` is currently tied to the wasmtime 17 release. Components that work with that release should work in `wepl`. 53 | -------------------------------------------------------------------------------- /docs/value-literals.md: -------------------------------------------------------------------------------- 1 | # Value Literals 2 | 3 | This document specifies how value literals for wit types are encoded. 4 | 5 | |Type|Example Values 6 | |---|--- 7 | |Bools|`true`, `false` 8 | |Ints|`123`, `-9` 9 | |Floats|`3.14`, `nan`, `-inf` 10 | |Chars|`'x'`, `'☃︎'`, `'\x00'` 11 | |Strings|`"abc"` 12 | |Tuples|`(123, "abc")` 13 | |Lists|`[1, 2, 3]` 14 | |Records|`{field-a: 1, field-b: "two"}` 15 | |Variants|`forever`, `days(30)` 16 | |Enums|`south`, `west` 17 | |Options|`"bare-form"`, `some("variant-form")`, `none` 18 | |Results|`ok(1)`, `err("oops")` 19 | |Flags|`{read, write}` 20 | |Resources|`stream(1)`(?) 21 | 22 | 23 | ## Bools 24 | 25 | Bools are encoded as literals `false` or `true`. 26 | 27 | ## Integers 28 | 29 | Integers are encoded as JSON numbers (without fraction or exponent). 30 | 31 | ## Floats 32 | 33 | Floats are encoded as JSON numbers or one of three literals 34 | encoding special vaules: `nan`, `inf`, `-inf` 35 | 36 | ## Chars 37 | 38 | Chars are encoded as `''`, where `` is one of: 39 | 40 | - a single UTF-8-encoded USV 41 | - an escaped ASCII char: `\'`, `\"`, `\\`, `\n`, `\r`, `\t`, `\x<2 hex digits>` 42 | 43 | > Escapes `\`→`\\` and `'`→`\'` are mandatory for chars. 44 | 45 | ## Strings 46 | 47 | Strings are encoded as a double-quote-delimited sequence of `` (as above). 48 | 49 | > Escapes `\` -> `\\` and `""` -> `\"` are mandatory for strings. 50 | 51 | ## Tuples 52 | 53 | Tuples are encoded as a sequence of parenthesized, 54 | comma-separated values. Trailing commas are permitted. 55 | 56 | `tuple` -> `(123, "abc",)` 57 | 58 | ## Lists 59 | 60 | Lists are encoded as a sequence of square-bracket-enclosed, 61 | comma-separated values. Trailing commas are permitted. 62 | 63 | `list` -> `['a', 'b', 'c',]` 64 | 65 | ## Records 66 | 67 | Records are encoded as a set of curly-brace-enclosed, 68 | comma-separated entries. Entries consist of a kebab-case 69 | field name, a colon, and a value. Trailing commas are 70 | permitted. Fields with `option` value `none` may be 71 | omitted entirely. 72 | 73 | ```clike 74 | record example { 75 | required-field: u8, 76 | optional-field: option, 77 | } 78 | ``` 79 | -> `{required-field: 123}` 80 | 81 | ## Variants 82 | 83 | Variants are encoded as a kebab-case case name. If the 84 | case has a payload, the name is followed by the 85 | parenthesized payload value. 86 | 87 | `variant error { eof, other(string) }` -> `other("oops")` 88 | 89 | ## Enums 90 | 91 | Enums are encoded in their variant form. 92 | 93 | `enum hand { left, right }` -> `left` 94 | 95 | ## Options 96 | 97 | Options may be encoded in their variant form 98 | (`some(1)`, `none`). A `some` value may also be encoded as 99 | the value itself, which may be applied recursively to nested 100 | options. 101 | 102 | `option` -> `123` = `some(123)` 103 | `option>` -> `123` = `some(123)` = `some(some(123))` 104 | 105 | > Note that certain nested option values can only be 106 | > expressed in the variant encoding form, like `some(none)`. 107 | 108 | 109 | ## Results 110 | 111 | Results are encoded in their variant form (`ok(1)`, `err("oops")`). 112 | An `ok` value may also be encoded as 113 | the value itself, which may be applied recursively to nested 114 | results. 115 | 116 | `result` -> `123` = `ok(123)` 117 | `result, string>` -> `123` = `ok(123)` = `ok(ok(123))` 118 | 119 | > Note that certain nested result values can only be 120 | > expressed in the variant encoding form, like `ok(err("oops"))`. 121 | 122 | 123 | ## Flags 124 | 125 | Flags are encoded as a set of curly-brace-enclosed, 126 | comma-separated, kebab-case names. Trailing commas are 127 | permitted. 128 | 129 | `flags perms { read, write, exec }` -> `{read, write,}` 130 | 131 | ## Resources 132 | 133 | At this time resources cannot be expressed as literals. -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" -------------------------------------------------------------------------------- /src/command.rs: -------------------------------------------------------------------------------- 1 | pub mod parser; 2 | mod tokenizer; 3 | use std::collections::HashMap; 4 | 5 | use anyhow::{bail, Context as _}; 6 | use colored::Colorize; 7 | use wasmtime::component::Val; 8 | 9 | use self::parser::Ident; 10 | use self::tokenizer::TokenKind; 11 | 12 | use super::runtime::Runtime; 13 | use super::wit::WorldResolver; 14 | use crate::evaluator::Evaluator; 15 | use crate::wit::Expansion; 16 | 17 | pub enum Cmd<'a> { 18 | BuiltIn { 19 | name: &'a str, 20 | args: Vec>, 21 | }, 22 | Eval(parser::Expr<'a>), 23 | Assign { 24 | ident: &'a str, 25 | value: parser::Expr<'a>, 26 | }, 27 | } 28 | 29 | impl<'a> Cmd<'a> { 30 | pub fn parse(input: &'a str) -> anyhow::Result>> { 31 | let tokens = tokenizer::Token::tokenize(input)?; 32 | let line = parser::Line::parse(tokens).map_err(|e| anyhow::anyhow!("{e}"))?; 33 | log::debug!("Parsed line: {line:?}"); 34 | match line { 35 | parser::Line::Expr(expr) => Ok(Some(Cmd::Eval(expr))), 36 | parser::Line::Assignment(ident, value) => Ok(Some(Cmd::Assign { ident, value })), 37 | parser::Line::BuiltIn(builtin) => Ok(Some(Cmd::BuiltIn { 38 | name: builtin.name, 39 | args: builtin.rest, 40 | })), 41 | } 42 | } 43 | 44 | /// Run the command 45 | /// 46 | /// Returns `Ok(true)` if the screen should be cleared 47 | pub fn run( 48 | self, 49 | runtime: &mut Runtime, 50 | resolver: &mut WorldResolver, 51 | scope: &mut HashMap, 52 | ) -> anyhow::Result { 53 | let mut eval = Evaluator::new(runtime, resolver, scope); 54 | match self { 55 | Cmd::Eval(expr) => match expr { 56 | parser::Expr::Literal(l) => { 57 | let val = eval.eval_literal(l, None)?; 58 | println!("{}: {}", format_val(&val), val_as_type(&val)); 59 | } 60 | parser::Expr::Ident(ident) => match scope.get(ident) { 61 | Some(val) => { 62 | println!("{}: {}", format_val(val), val_as_type(val)) 63 | } 64 | None => { 65 | anyhow::bail!("no identifier '{ident}' in scope") 66 | } 67 | }, 68 | parser::Expr::FunctionCall(func) => { 69 | let results = eval.call_func(func.ident, func.args)?; 70 | println!( 71 | "{}", 72 | results 73 | .into_iter() 74 | .map(|v| format_val(&v)) 75 | .collect::>() 76 | .join("\n") 77 | ) 78 | } 79 | }, 80 | Cmd::Assign { ident, value } => { 81 | let val = eval.eval(value, None)?; 82 | println!("{}: {}", ident, val_as_type(&val)); 83 | scope.insert(ident.into(), val); 84 | } 85 | Cmd::BuiltIn { 86 | name: "exports", 87 | args, 88 | } => { 89 | let &[] = args.as_slice() else { 90 | bail!( 91 | "wrong number of arguments to exports function. Expected 0 got {}", 92 | args.len() 93 | ) 94 | }; 95 | for (export_name, export) in resolver.world().exports.iter() { 96 | let export_name = resolver.world_item_name(export_name); 97 | if let Some(ty) = format_world_item(export, resolver) { 98 | println!("{}: {ty}", export_name.bold()); 99 | } 100 | } 101 | } 102 | Cmd::BuiltIn { 103 | name: "imports", 104 | args, 105 | } => { 106 | let include_wasi = match args.as_slice() { 107 | [] => true, 108 | [t] => match t.token() { 109 | TokenKind::Flag("no-wasi") => false, 110 | TokenKind::Flag(flag) => { 111 | bail!("unrecognized flag for imports builtin '{flag}'") 112 | } 113 | _ => bail!("unrecognized token {}", t.input.str), 114 | }, 115 | _ => { 116 | bail!( 117 | "wrong number of arguments to imports function. Expected 1 got {}", 118 | args.len() 119 | ) 120 | } 121 | }; 122 | for (import_name, import) in resolver.imports(include_wasi) { 123 | let import_name = resolver.world_item_name(import_name); 124 | if let Some(ty) = format_world_item(import, resolver) { 125 | println!("{}: {ty}", import_name.bold()); 126 | } 127 | } 128 | } 129 | Cmd::BuiltIn { name: "type", args } => { 130 | match args.as_slice() { 131 | &[token] => { 132 | let TokenKind::Ident(name) = token.token() else { 133 | bail!("unrecognized token") 134 | }; 135 | let types = resolver.types_by_name(name); 136 | for (interface, ty) in &types { 137 | let typ = resolver.display_wit_type_def(ty, Expansion::Expanded(1)); 138 | let name = &ty.name; 139 | let interface = interface.and_then(|i| resolver.interface_name(i)); 140 | let ident = match (interface, name) { 141 | (Some(i), Some(n)) => format!("{i}#{n}: "), 142 | (None, Some(n)) => format!("{n}: "), 143 | _ => todo!(), 144 | }; 145 | println!("{ident}{typ}"); 146 | } 147 | } 148 | _ => bail!( 149 | "wrong number of arguments to inspect function. Expected 1 got {}", 150 | args.len() 151 | ), 152 | }; 153 | } 154 | Cmd::BuiltIn { 155 | name: "compose", 156 | args, 157 | } => { 158 | let &[token] = args.as_slice() else { 159 | bail!( 160 | "wrong number of arguments to compose function. Expected 1 got {}", 161 | args.len() 162 | ) 163 | }; 164 | let TokenKind::String(path) = token.token() else { 165 | bail!("unrecognized token {}", token.input.str); 166 | }; 167 | let adapter = 168 | std::fs::read(path).context("could not read path to adapter module")?; 169 | runtime.compose(&adapter)?; 170 | *resolver = WorldResolver::from_bytes(runtime.component_bytes())?; 171 | } 172 | Cmd::BuiltIn { name: "link", args } => { 173 | let mut args = args.into_iter().collect(); 174 | let Ok(Some(import_ident)) = Ident::try_parse(&mut args) else { 175 | bail!("import_ident is not a proper item identifier"); 176 | }; 177 | let Ok(Some(export_ident)) = Ident::try_parse(&mut args) else { 178 | bail!("export_ident is not a proper item identifier"); 179 | }; 180 | 181 | let Some(TokenKind::String(component)) = args.pop_front().map(|t| t.token()) else { 182 | bail!("component path is not a string"); 183 | }; 184 | let component_bytes = std::fs::read(component) 185 | .with_context(|| format!("could not read component '{component}'"))?; 186 | runtime.stub(resolver, import_ident, export_ident, &component_bytes)?; 187 | } 188 | Cmd::BuiltIn { 189 | name: "inspect", 190 | args, 191 | } => { 192 | let mut args = args.into_iter().collect(); 193 | let Ok(Some(ident)) = Ident::try_parse(&mut args) else { 194 | bail!("ident is not a proper item identifier"); 195 | }; 196 | match ident { 197 | Ident::Item(ident) => { 198 | let f = resolver 199 | .exported_function(ident) 200 | .or_else(|| resolver.imported_function(ident)); 201 | match f { 202 | Some(f) => println!("{}", format_function(f, resolver)), 203 | None => bail!("Could not find imported or exported function '{ident}'"), 204 | } 205 | } 206 | Ident::Interface(ident) => { 207 | let i = resolver 208 | .exported_interface(ident) 209 | .or_else(|| resolver.imported_interface(ident)); 210 | match i { 211 | Some(f) => println!("{}", format_interface(f, resolver)), 212 | None => { 213 | bail!("Could not find imported or exported interface '{ident}'") 214 | } 215 | } 216 | } 217 | } 218 | } 219 | Cmd::BuiltIn { 220 | name: "help", 221 | args: _, 222 | } => print_help(), 223 | Cmd::BuiltIn { 224 | name: "clear", 225 | args: _, 226 | } => return Ok(true), 227 | Cmd::BuiltIn { name, args: _ } => { 228 | bail!("Unrecognized built-in function '{name}'") 229 | } 230 | } 231 | Ok(false) 232 | } 233 | } 234 | 235 | fn print_help() { 236 | println!("Calling imports can be done like so: 237 | 238 | > my-func(my-arg) 239 | 240 | Variables can be saved as well: 241 | 242 | > my-var = my-func(my-arg) 243 | 244 | There are also builtin functions that can be called with a preceding '.'. Supported functions include: 245 | .imports print a list of all the component's imports 246 | .exports print a list of all the component's exports 247 | .link $function $wasm satisfy the imported function `$func` with an export from the wasm component `$wasm` 248 | .compose $adapter satisfy imports with the supplied adapter module (e.g., to compose with WASI-Virt adapter) 249 | .inspect $item inspect an item `$item` in scope (`?` is alias for this built-in)") 250 | } 251 | 252 | fn format_world_item(item: &wit_parser::WorldItem, resolver: &WorldResolver) -> Option { 253 | match item { 254 | wit_parser::WorldItem::Function(f) => Some(format_function(f, resolver)), 255 | wit_parser::WorldItem::Interface { id, .. } => { 256 | let interface = resolver.interface_by_id(*id).unwrap(); 257 | if interface.functions.is_empty() { 258 | return None; 259 | } 260 | let output = format_interface(interface, resolver); 261 | Some(output) 262 | } 263 | wit_parser::WorldItem::Type(_) => None, 264 | } 265 | } 266 | 267 | fn format_interface(interface: &wit_parser::Interface, resolver: &WorldResolver) -> String { 268 | use std::fmt::Write; 269 | let mut output = String::from("{\n"); 270 | for (_, fun) in &interface.functions { 271 | writeln!( 272 | &mut output, 273 | " {}: {}", 274 | fun.name.bold(), 275 | format_function(fun, resolver) 276 | ) 277 | .unwrap(); 278 | } 279 | output.push('}'); 280 | output 281 | } 282 | 283 | fn format_function(f: &wit_parser::Function, resolver: &WorldResolver) -> String { 284 | let mut params = Vec::new(); 285 | for (param_name, param_type) in &f.params { 286 | let ty = resolver.display_wit_type(param_type, Expansion::Collapsed); 287 | params.push(format!("{param_name}: {}", ty.italic())); 288 | } 289 | let params = params.join(", "); 290 | let rets = match &f.results { 291 | wit_parser::Results::Anon(t) => { 292 | let t = resolver.display_wit_type(t, Expansion::Collapsed); 293 | format!(" -> {}", t.italic()) 294 | } 295 | wit_parser::Results::Named(n) if n.is_empty() => String::new(), 296 | wit_parser::Results::Named(params) => { 297 | let params = params 298 | .iter() 299 | .map(|(name, t)| { 300 | let t = resolver.display_wit_type(t, Expansion::Collapsed); 301 | format!("{name}: {t}") 302 | }) 303 | .collect::>() 304 | .join(", "); 305 | format!(" -> {params}") 306 | } 307 | }; 308 | format!("func({params}){rets}") 309 | } 310 | 311 | fn format_val(val: &Val) -> String { 312 | match val { 313 | Val::String(s) => format!(r#""{s}""#), 314 | Val::Bool(b) => b.to_string(), 315 | Val::U8(u) => u.to_string(), 316 | Val::U16(u) => u.to_string(), 317 | Val::U32(u) => u.to_string(), 318 | Val::U64(u) => u.to_string(), 319 | Val::S8(s) => s.to_string(), 320 | Val::S16(s) => s.to_string(), 321 | Val::S32(s) => s.to_string(), 322 | Val::S64(s) => s.to_string(), 323 | Val::Float32(f) => f.to_string(), 324 | Val::Float64(f) => f.to_string(), 325 | Val::Char(c) => c.to_string(), 326 | Val::Option(o) => match o { 327 | Some(o) => format!("some({})", format_val(o)), 328 | None => "none".into(), 329 | }, 330 | Val::Result(r) => match r { 331 | Ok(Some(o)) => format!("ok({})", format_val(o)), 332 | Ok(None) => "ok".to_string(), 333 | Err(Some(e)) => format!("err({})", format_val(e)), 334 | Err(None) => "err".to_string(), 335 | }, 336 | Val::List(l) => { 337 | let items = l.iter().map(format_val).collect::>().join(", "); 338 | format!("[{items}]") 339 | } 340 | Val::Record(r) => { 341 | let fields = r 342 | .iter() 343 | .map(|(key, value)| format!("{}: {}", key, format_val(value))) 344 | .collect::>() 345 | .join(", "); 346 | format!("{{ {fields} }}") 347 | } 348 | Val::Tuple(_) => todo!(), 349 | Val::Variant(_, _) => todo!(), 350 | Val::Enum(_) => todo!(), 351 | Val::Flags(_) => todo!(), 352 | Val::Resource(_) => todo!(), 353 | } 354 | } 355 | 356 | fn val_as_type(val: &Val) -> &'static str { 357 | match val { 358 | Val::String(_) => "string", 359 | Val::Bool(_) => "bool", 360 | Val::U8(_) => "u8", 361 | Val::U16(_) => "u16", 362 | Val::U32(_) => "u32", 363 | Val::U64(_) => "u64", 364 | Val::S8(_) => "s8", 365 | Val::S16(_) => "s16", 366 | Val::S32(_) => "s32", 367 | Val::S64(_) => "s64", 368 | Val::Float32(_) => "float32", 369 | Val::Float64(_) => "float64", 370 | Val::Char(_) => "char", 371 | Val::Option(_) => "option", 372 | Val::Result(_) => "result", 373 | Val::List(_) => "list", 374 | Val::Record(_) => "record", 375 | Val::Tuple(_) => todo!(), 376 | Val::Variant(_, _) => todo!(), 377 | Val::Enum(_) => todo!(), 378 | Val::Flags(_) => todo!(), 379 | Val::Resource(_) => todo!(), 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /src/command/parser.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | 3 | use crate::command::tokenizer::TokenKind; 4 | 5 | use super::tokenizer::Token; 6 | 7 | #[derive(Debug, PartialEq)] 8 | pub enum Line<'a> { 9 | Expr(Expr<'a>), 10 | BuiltIn(BuiltIn<'a>), 11 | Assignment(&'a str, Expr<'a>), 12 | } 13 | 14 | impl<'a> Line<'a> { 15 | pub fn parse(mut tokens: VecDeque>) -> Result, ParserError<'a>> { 16 | let result = match BuiltIn::try_parse(&mut tokens)? { 17 | Some(builtin) => Ok(Self::BuiltIn(builtin)), 18 | None => match Self::try_parse_assignment(&mut tokens)? { 19 | Some((ident, expr)) => Ok(Self::Assignment(ident, expr)), 20 | None => match Expr::try_parse(&mut tokens)? { 21 | Some(e) => Ok(Self::Expr(e)), 22 | None => { 23 | return match tokens.front() { 24 | Some(t) => Err(ParserError::UnexpectedToken(*t)), 25 | None => Err(ParserError::UnexpectedEndOfInput), 26 | } 27 | } 28 | }, 29 | }, 30 | }; 31 | if !tokens.is_empty() { 32 | return Err(ParserError::RemainingInput); 33 | } 34 | result 35 | } 36 | 37 | fn try_parse_assignment( 38 | tokens: &mut VecDeque>, 39 | ) -> Result)>, ParserError<'a>> { 40 | let Some(token) = tokens.front() else { 41 | return Ok(None); 42 | }; 43 | let TokenKind::Ident(ident) = token.token() else { 44 | return Ok(None); 45 | }; 46 | let token = *token; 47 | let _ = tokens.pop_front(); 48 | if matches!(tokens.front().map(|t| t.token()), Some(TokenKind::Equal)) { 49 | let _ = tokens.pop_front(); 50 | match Expr::try_parse(tokens)? { 51 | Some(e) => Ok(Some((ident, e))), 52 | None => Err(ParserError::ExpectedExpr), 53 | } 54 | } else { 55 | tokens.push_front(token); 56 | Ok(None) 57 | } 58 | } 59 | } 60 | 61 | #[derive(Debug, PartialEq)] 62 | pub struct BuiltIn<'a> { 63 | pub name: &'a str, 64 | pub rest: Vec>, 65 | } 66 | 67 | impl<'a> BuiltIn<'a> { 68 | fn try_parse(tokens: &mut VecDeque>) -> Result>, ParserError<'a>> { 69 | let Some(TokenKind::Builtin(ident)) = tokens.front().map(|t| t.token()) else { 70 | return Ok(None); 71 | }; 72 | tokens.pop_front(); 73 | Ok(Some(BuiltIn { 74 | name: ident, 75 | rest: tokens.drain(..).collect(), 76 | })) 77 | } 78 | } 79 | 80 | #[derive(Debug, PartialEq)] 81 | pub enum ParserError<'a> { 82 | UnexpectedToken(Token<'a>), 83 | UnexpectedEndOfInput, 84 | RemainingInput, 85 | ExpectedExpr, 86 | } 87 | 88 | impl std::error::Error for ParserError<'_> {} 89 | 90 | impl std::fmt::Display for ParserError<'_> { 91 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 92 | match self { 93 | ParserError::UnexpectedToken(_) => f.write_str("unexpected token"), 94 | ParserError::UnexpectedEndOfInput => f.write_str("unexpected end of input"), 95 | ParserError::RemainingInput => f.write_str("remaining input"), 96 | ParserError::ExpectedExpr => f.write_str("unexpected expression"), 97 | } 98 | } 99 | } 100 | 101 | #[derive(Debug, PartialEq)] 102 | pub enum Expr<'a> { 103 | FunctionCall(FunctionCall<'a>), 104 | Ident(&'a str), 105 | Literal(Literal<'a>), 106 | } 107 | 108 | impl<'a> Expr<'a> { 109 | fn try_parse(input: &mut VecDeque>) -> Result>, ParserError<'a>> { 110 | let Some(first) = input.front() else { 111 | return Ok(None); 112 | }; 113 | match first.token() { 114 | TokenKind::String(s) => { 115 | input.pop_front(); 116 | Ok(Some(Expr::Literal(Literal::String(s)))) 117 | } 118 | TokenKind::Number(n) => { 119 | input.pop_front(); 120 | Ok(Some(Expr::Literal(Literal::Number(n)))) 121 | } 122 | TokenKind::OpenBracket => { 123 | input.pop_front(); 124 | enum State { 125 | ExpectExpr, 126 | ExpectComma, 127 | } 128 | let mut state = State::ExpectExpr; 129 | let mut items = vec![]; 130 | while let Some(token) = input.front() { 131 | match token.token() { 132 | TokenKind::ClosedBracket => { 133 | input.pop_front(); 134 | return Ok(Some(Expr::Literal(Literal::List(List { items })))); 135 | } 136 | TokenKind::Comma if matches!(state, State::ExpectComma) => { 137 | input.pop_front(); 138 | state = State::ExpectExpr; 139 | } 140 | _ => { 141 | let expr = Expr::try_parse(input)?; 142 | if let Some(expr) = expr { 143 | items.push(expr); 144 | state = State::ExpectComma; 145 | } else { 146 | return Err(ParserError::UnexpectedEndOfInput); 147 | } 148 | } 149 | } 150 | } 151 | Err(ParserError::UnexpectedEndOfInput) 152 | } 153 | TokenKind::OpenBrace => { 154 | input.pop_front(); 155 | #[allow(clippy::enum_variant_names)] 156 | enum State<'a> { 157 | ExpectIdent, 158 | ExpectColon(&'a str), 159 | ExpectExpr(&'a str), 160 | ExpectComma, 161 | } 162 | let mut state = State::ExpectIdent; 163 | let mut fields = vec![]; 164 | while let Some(token) = input.front() { 165 | match (token.token(), state) { 166 | (TokenKind::ClosedBrace, State::ExpectComma | State::ExpectIdent) => { 167 | input.pop_front(); 168 | return Ok(Some(Expr::Literal(Literal::Record(Record { fields })))); 169 | } 170 | (TokenKind::Comma, State::ExpectComma) => { 171 | input.pop_front(); 172 | state = State::ExpectIdent; 173 | } 174 | (TokenKind::Colon, State::ExpectColon(ident)) => { 175 | input.pop_front(); 176 | state = State::ExpectExpr(ident); 177 | } 178 | (_, State::ExpectIdent) => { 179 | let ident = Literal::parse_ident(input)?; 180 | state = State::ExpectColon(ident); 181 | } 182 | (_, State::ExpectExpr(ident)) => { 183 | let expr = Expr::try_parse(input)?; 184 | if let Some(expr) = expr { 185 | fields.push((ident, expr)); 186 | state = State::ExpectComma; 187 | } else { 188 | return Err(ParserError::UnexpectedEndOfInput); 189 | } 190 | } 191 | _ => return Err(ParserError::UnexpectedToken(*token)), 192 | } 193 | } 194 | Err(ParserError::UnexpectedEndOfInput) 195 | } 196 | TokenKind::Ident(_) => { 197 | let func = FunctionCall::try_parse(input)?; 198 | match func { 199 | Some(f) => Ok(Some(Expr::FunctionCall(f))), 200 | None => Ok(Some(Expr::Ident(Literal::parse_ident(input)?))), 201 | } 202 | } 203 | 204 | _ => Ok(None), 205 | } 206 | } 207 | } 208 | 209 | #[derive(Debug, PartialEq)] 210 | pub struct FunctionCall<'a> { 211 | pub ident: ItemIdent<'a>, 212 | pub args: Vec>, 213 | } 214 | 215 | impl<'a> FunctionCall<'a> { 216 | fn try_parse(input: &mut VecDeque>) -> Result, ParserError<'a>> { 217 | let original = input.clone(); 218 | let Some(function_ident) = ItemIdent::try_parse(input)? else { 219 | return Ok(None); 220 | }; 221 | let next = input.front(); 222 | if next.map(|t| t.token()) != Some(TokenKind::OpenParen) { 223 | // If we failed to find an open paren then we need to completely bail 224 | // on function parsing which means restoring the input state back to 225 | // its original form. 226 | *input = original; 227 | return Ok(None); 228 | } 229 | expect_token(input, |t| t == TokenKind::OpenParen)?; 230 | let mut args = Vec::new(); 231 | loop { 232 | let Some(expr) = Expr::try_parse(input)? else { 233 | break; 234 | }; 235 | args.push(expr); 236 | if input.front().map(|t| t.token()) != Some(TokenKind::Comma) { 237 | break; 238 | } 239 | input.pop_front(); 240 | } 241 | expect_token(input, |t| t == TokenKind::ClosedParen)?; 242 | Ok(Some(FunctionCall { 243 | ident: function_ident, 244 | args, 245 | })) 246 | } 247 | } 248 | 249 | fn expect_token<'a>( 250 | input: &mut VecDeque>, 251 | pred: impl FnOnce(TokenKind<'a>) -> bool, 252 | ) -> Result<(), ParserError<'a>> { 253 | let Some(token) = input.pop_front() else { 254 | return Err(ParserError::UnexpectedEndOfInput); 255 | }; 256 | if !pred(token.token()) { 257 | return Err(ParserError::UnexpectedToken(token)); 258 | } 259 | Ok(()) 260 | } 261 | 262 | #[derive(Debug, PartialEq, Copy, Clone)] 263 | pub enum Ident<'a> { 264 | Item(ItemIdent<'a>), 265 | Interface(InterfaceIdent<'a>), 266 | } 267 | 268 | impl<'a> Ident<'a> { 269 | pub(crate) fn try_parse( 270 | input: &mut VecDeque>, 271 | ) -> Result>, ParserError<'a>> { 272 | match ItemIdent::try_parse(input)? { 273 | Some(i) => Ok(Some(Self::Item(i))), 274 | None => Ok(InterfaceIdent::try_parse(input)?.map(Self::Interface)), 275 | } 276 | } 277 | } 278 | 279 | #[derive(Debug, PartialEq, Copy, Clone)] 280 | pub struct ItemIdent<'a> { 281 | pub interface: Option>, 282 | pub item: &'a str, 283 | } 284 | 285 | impl<'a> ItemIdent<'a> { 286 | fn try_parse(input: &mut VecDeque>) -> Result, ParserError<'a>> { 287 | let interface = InterfaceIdent::try_parse(input)?; 288 | match interface { 289 | Some(i) if i.package.is_none() => { 290 | if input.front().map(|t| t.token()) == Some(TokenKind::Hash) { 291 | input.pop_front(); 292 | let ident = Literal::parse_ident(input)?; 293 | Ok(Some(ItemIdent { 294 | interface: Some(i), 295 | item: ident, 296 | })) 297 | } else { 298 | // We parsed the function ident as the interface ident 299 | // Map the interface ident to the function ident 300 | Ok(Some(ItemIdent { 301 | interface: None, 302 | item: i.interface, 303 | })) 304 | } 305 | } 306 | Some(i) => { 307 | // if we parse an interface id with a full package, we must 308 | // be expecting a `#` next with the function ident 309 | match input.pop_front() { 310 | Some(t) if t.token() == TokenKind::Hash => { 311 | let ident = Literal::parse_ident(input)?; 312 | Ok(Some(ItemIdent { 313 | interface: Some(i), 314 | item: ident, 315 | })) 316 | } 317 | Some(t) => Err(ParserError::UnexpectedToken(t)), 318 | None => Err(ParserError::UnexpectedEndOfInput), 319 | } 320 | } 321 | 322 | None => Ok(None), 323 | } 324 | } 325 | } 326 | 327 | impl std::fmt::Display for ItemIdent<'_> { 328 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 329 | if let Some(interface) = self.interface { 330 | write!(f, "{interface}#")? 331 | } 332 | write!(f, "{}", self.item) 333 | } 334 | } 335 | 336 | #[derive(Debug, PartialEq, Clone, Copy)] 337 | pub struct InterfaceIdent<'a> { 338 | package: Option<(&'a str, &'a str)>, 339 | interface: &'a str, 340 | } 341 | 342 | impl<'a> InterfaceIdent<'a> { 343 | fn try_parse<'b>(input: &'b mut VecDeque>) -> Result, ParserError<'a>> { 344 | #[derive(Debug)] 345 | #[allow(clippy::enum_variant_names)] 346 | enum State<'a> { 347 | ExpectFirst, 348 | ExpectColon(&'a str), 349 | ExpectSecond(&'a str), 350 | ExpectSlash(&'a str, &'a str), 351 | ExpectThird(&'a str, &'a str), 352 | } 353 | let mut state = State::ExpectFirst; 354 | loop { 355 | let token = input.front(); 356 | match (token.map(|t| t.token()), state) { 357 | (Some(TokenKind::Ident(i)), State::ExpectFirst) => { 358 | input.pop_front(); 359 | state = State::ExpectColon(i); 360 | } 361 | (Some(TokenKind::Colon), State::ExpectColon(first)) => { 362 | input.pop_front(); 363 | state = State::ExpectSecond(first); 364 | } 365 | (Some(TokenKind::Ident(second)), State::ExpectSecond(first)) => { 366 | input.pop_front(); 367 | state = State::ExpectSlash(first, second); 368 | } 369 | (Some(TokenKind::Slash), State::ExpectSlash(first, second)) => { 370 | input.pop_front(); 371 | state = State::ExpectThird(first, second); 372 | } 373 | (Some(TokenKind::Ident(third)), State::ExpectThird(first, second)) => { 374 | input.pop_front(); 375 | return Ok(Some(InterfaceIdent { 376 | package: Some((first, second)), 377 | interface: third, 378 | })); 379 | } 380 | (_, State::ExpectColon(first)) => { 381 | return Ok(Some(InterfaceIdent { 382 | package: None, 383 | interface: first, 384 | })); 385 | } 386 | (_, State::ExpectFirst) => return Ok(None), 387 | (Some(_), _) => return Err(ParserError::UnexpectedToken(*token.unwrap())), 388 | _ => return Err(ParserError::UnexpectedEndOfInput), 389 | } 390 | } 391 | } 392 | } 393 | 394 | impl std::fmt::Display for InterfaceIdent<'_> { 395 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 396 | if let Some((namespace, package)) = self.package { 397 | write!(f, "{namespace}:{package}/")?; 398 | } 399 | write!(f, "{}", self.interface) 400 | } 401 | } 402 | 403 | #[derive(Debug, PartialEq)] 404 | pub enum Literal<'a> { 405 | String(&'a str), 406 | Number(usize), 407 | List(List<'a>), 408 | Record(Record<'a>), 409 | } 410 | 411 | impl<'a> Literal<'a> { 412 | fn parse_ident(input: &mut VecDeque>) -> Result<&'a str, ParserError<'a>> { 413 | let Some(token) = input.front() else { 414 | return Err(ParserError::UnexpectedEndOfInput); 415 | }; 416 | match token.token() { 417 | TokenKind::Ident(i) => { 418 | input.pop_front(); 419 | Ok(i) 420 | } 421 | _ => Err(ParserError::UnexpectedToken(*token)), 422 | } 423 | } 424 | } 425 | 426 | #[derive(Debug, PartialEq)] 427 | pub struct List<'a> { 428 | pub items: Vec>, 429 | } 430 | 431 | impl<'a> From>> for List<'a> { 432 | fn from(items: Vec>) -> Self { 433 | Self { items } 434 | } 435 | } 436 | 437 | #[derive(Debug, PartialEq)] 438 | pub struct Record<'a> { 439 | pub fields: Vec<(&'a str, Expr<'a>)>, 440 | } 441 | 442 | impl<'a> From)>> for Record<'a> { 443 | fn from(fields: Vec<(&'a str, Expr<'a>)>) -> Self { 444 | Self { fields } 445 | } 446 | } 447 | 448 | #[cfg(test)] 449 | mod tests { 450 | use std::vec; 451 | 452 | use crate::command::tokenizer::{SpannedStr, TokenKind}; 453 | 454 | use super::*; 455 | 456 | fn dummy_spanned_str() -> SpannedStr<'static> { 457 | SpannedStr { str: "", offset: 0 } 458 | } 459 | 460 | fn token(kind: TokenKind<'static>) -> Token<'static> { 461 | Token { 462 | input: dummy_spanned_str(), 463 | token: kind, 464 | } 465 | } 466 | 467 | fn tokens(tokens: impl IntoIterator>) -> VecDeque> { 468 | tokens.into_iter().map(token).collect() 469 | } 470 | 471 | fn parse( 472 | ts: impl IntoIterator>, 473 | ) -> Result, ParserError<'static>> { 474 | Line::parse(tokens(ts)) 475 | } 476 | 477 | #[test] 478 | fn parse_string_literals() { 479 | let line = parse([TokenKind::String("hello-world")]).unwrap(); 480 | assert_eq!( 481 | line, 482 | Line::Expr(Expr::Literal(Literal::String("hello-world"))) 483 | ); 484 | } 485 | 486 | #[test] 487 | fn parse_list_literals() { 488 | let list_of_string = Line::Expr(Expr::Literal(Literal::List( 489 | vec![Expr::Literal(Literal::String("hello-world"))].into(), 490 | ))); 491 | let line = parse([ 492 | TokenKind::OpenBracket, 493 | TokenKind::String("hello-world"), 494 | TokenKind::ClosedBracket, 495 | ]) 496 | .unwrap(); 497 | assert_eq!(line, list_of_string); 498 | 499 | let line = parse([ 500 | TokenKind::OpenBracket, 501 | TokenKind::String("hello-world"), 502 | TokenKind::Comma, 503 | TokenKind::ClosedBracket, 504 | ]) 505 | .unwrap(); 506 | assert_eq!(line, list_of_string); 507 | 508 | let err = parse([ 509 | TokenKind::OpenBracket, 510 | TokenKind::String("hello-world"), 511 | TokenKind::Comma, 512 | ]) 513 | .unwrap_err(); 514 | assert_eq!(err, ParserError::UnexpectedEndOfInput); 515 | } 516 | 517 | #[test] 518 | fn parse_record_literals() { 519 | let record = Line::Expr(Expr::Literal(Literal::Record( 520 | vec![("foo", Expr::Literal(Literal::String("bar")))].into(), 521 | ))); 522 | let line = parse([ 523 | TokenKind::OpenBrace, 524 | TokenKind::Ident("foo"), 525 | TokenKind::Colon, 526 | TokenKind::String("bar"), 527 | TokenKind::ClosedBrace, 528 | ]) 529 | .unwrap(); 530 | assert_eq!(line, record); 531 | 532 | let line = parse([ 533 | TokenKind::OpenBrace, 534 | TokenKind::Ident("foo"), 535 | TokenKind::Colon, 536 | TokenKind::String("bar"), 537 | TokenKind::Comma, 538 | TokenKind::ClosedBrace, 539 | ]) 540 | .unwrap(); 541 | assert_eq!(line, record); 542 | 543 | let err = parse([ 544 | TokenKind::OpenBrace, 545 | TokenKind::Ident("foo"), 546 | TokenKind::String("bar"), 547 | TokenKind::ClosedBrace, 548 | ]) 549 | .unwrap_err(); 550 | assert_eq!( 551 | err, 552 | ParserError::UnexpectedToken(token(TokenKind::String("bar"))) 553 | ); 554 | } 555 | 556 | #[test] 557 | fn parse_function_calls() { 558 | let function = Line::Expr(Expr::FunctionCall(FunctionCall { 559 | ident: ItemIdent { 560 | interface: Some(InterfaceIdent { 561 | package: Some(("foo", "bar")), 562 | interface: "baz", 563 | }), 564 | item: "qux", 565 | }, 566 | args: vec![], 567 | })); 568 | let line = parse([ 569 | TokenKind::Ident("foo"), 570 | TokenKind::Colon, 571 | TokenKind::Ident("bar"), 572 | TokenKind::Slash, 573 | TokenKind::Ident("baz"), 574 | TokenKind::Hash, 575 | TokenKind::Ident("qux"), 576 | TokenKind::OpenParen, 577 | TokenKind::ClosedParen, 578 | ]) 579 | .unwrap(); 580 | assert_eq!(line, function); 581 | 582 | let function = Line::Expr(Expr::FunctionCall(FunctionCall { 583 | ident: ItemIdent { 584 | interface: None, 585 | item: "qux", 586 | }, 587 | args: vec![], 588 | })); 589 | let line = parse([ 590 | TokenKind::Ident("qux"), 591 | TokenKind::OpenParen, 592 | TokenKind::ClosedParen, 593 | ]) 594 | .unwrap(); 595 | assert_eq!(line, function); 596 | 597 | let function = Line::Expr(Expr::FunctionCall(FunctionCall { 598 | ident: ItemIdent { 599 | interface: None, 600 | item: "foo", 601 | }, 602 | args: vec![Expr::FunctionCall(FunctionCall { 603 | ident: ItemIdent { 604 | interface: None, 605 | item: "bar", 606 | }, 607 | args: vec![], 608 | })], 609 | })); 610 | let line = parse([ 611 | TokenKind::Ident("foo"), 612 | TokenKind::OpenParen, 613 | TokenKind::Ident("bar"), 614 | TokenKind::OpenParen, 615 | TokenKind::ClosedParen, 616 | TokenKind::ClosedParen, 617 | ]) 618 | .unwrap(); 619 | assert_eq!(line, function); 620 | 621 | let err = parse([ 622 | TokenKind::Ident("foo"), 623 | TokenKind::Colon, 624 | TokenKind::Ident("bar"), 625 | TokenKind::OpenParen, 626 | TokenKind::ClosedParen, 627 | ]) 628 | .unwrap_err(); 629 | assert_eq!( 630 | err, 631 | ParserError::UnexpectedToken(token(TokenKind::OpenParen)) 632 | ); 633 | 634 | let err = parse([ 635 | TokenKind::Ident("foo"), 636 | TokenKind::Colon, 637 | TokenKind::Ident("bar"), 638 | TokenKind::Hash, 639 | TokenKind::Ident("baz"), 640 | TokenKind::OpenParen, 641 | TokenKind::ClosedParen, 642 | ]) 643 | .unwrap_err(); 644 | assert_eq!(err, ParserError::UnexpectedToken(token(TokenKind::Hash))); 645 | } 646 | 647 | #[test] 648 | fn parse_ident_expr() { 649 | let line = parse([TokenKind::Ident("foo")]).unwrap(); 650 | assert_eq!(line, Line::Expr(Expr::Ident("foo"))); 651 | } 652 | 653 | #[test] 654 | fn parse_builtin() { 655 | let line = parse([TokenKind::Builtin("foo"), TokenKind::Ident("foo")]).unwrap(); 656 | assert_eq!( 657 | line, 658 | Line::BuiltIn(BuiltIn { 659 | name: "foo", 660 | rest: vec![token(TokenKind::Ident("foo"))] 661 | }) 662 | ); 663 | } 664 | 665 | #[test] 666 | fn parse_assignment() { 667 | let line = parse([ 668 | TokenKind::Ident("foo"), 669 | TokenKind::Equal, 670 | TokenKind::String("bar"), 671 | ]) 672 | .unwrap(); 673 | assert_eq!( 674 | line, 675 | Line::Assignment("foo", Expr::Literal(Literal::String("bar"))) 676 | ); 677 | } 678 | } 679 | -------------------------------------------------------------------------------- /src/command/tokenizer.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::VecDeque, fmt::Write, ops::Deref}; 2 | 3 | #[derive(Clone, Copy, Debug, PartialEq)] 4 | pub struct Token<'a> { 5 | pub input: SpannedStr<'a>, 6 | pub token: TokenKind<'a>, 7 | } 8 | 9 | impl<'a> Token<'a> { 10 | pub fn token(&self) -> TokenKind<'a> { 11 | self.token 12 | } 13 | } 14 | 15 | #[derive(Clone, Copy, Debug, PartialEq)] 16 | pub enum TokenKind<'a> { 17 | String(&'a str), 18 | Ident(&'a str), 19 | Builtin(&'a str), 20 | Flag(&'a str), 21 | Number(usize), 22 | Equal, 23 | OpenParen, 24 | ClosedParen, 25 | Slash, 26 | Hash, 27 | Colon, 28 | OpenBracket, 29 | ClosedBracket, 30 | OpenBrace, 31 | ClosedBrace, 32 | Comma, 33 | Period, 34 | } 35 | 36 | impl<'a> Token<'a> { 37 | pub fn tokenize(input: &'a str) -> Result>, TokenizeError> { 38 | let mut tokens = VecDeque::new(); 39 | let mut rest = SpannedStr { 40 | str: input, 41 | offset: 0, 42 | }; 43 | while !rest.is_empty() { 44 | let (new_rest, token) = Token::next(rest)?; 45 | if let Some(token) = token { 46 | tokens.push_back(token); 47 | } 48 | rest = new_rest; 49 | } 50 | Ok(tokens) 51 | } 52 | 53 | fn next(rest: SpannedStr<'a>) -> Result<(SpannedStr<'a>, Option>), TokenizeError> { 54 | let mut chars = rest.chars().peekable(); 55 | let original_offset = rest.offset; 56 | let Some(first) = chars.next() else { 57 | return Ok((rest, None)); 58 | }; 59 | let (offset, token_kind) = match first { 60 | '"' => { 61 | let len: usize = chars.take_while(|c| *c != '"').map(|c| c.len_utf8()).sum(); 62 | let offset = 2 * '"'.len_utf8() + len; 63 | let str = &rest.str[1..(offset - 1)]; 64 | (offset, Some(TokenKind::String(str))) 65 | } 66 | c if c.is_ascii_alphabetic() => { 67 | let len: usize = chars 68 | .take_while(|c| c.is_ascii_alphabetic() || *c == '-') 69 | .map(|c| c.len_utf8()) 70 | .sum(); 71 | let offset = c.len_utf8() + len; 72 | let str = &rest.str[..offset]; 73 | if str.ends_with('-') { 74 | return Err(TokenizeError::UnexpectedChar( 75 | '-', 76 | original_offset + offset - 1, 77 | )); 78 | } 79 | (offset, Some(TokenKind::Ident(str))) 80 | } 81 | c if c.is_ascii_digit() => { 82 | let len: usize = chars 83 | .take_while(|c| c.is_ascii_digit()) 84 | .map(|c| c.len_utf8()) 85 | .sum(); 86 | let offset = c.len_utf8() + len; 87 | let num = rest.str[..offset] 88 | .parse() 89 | .expect("failed to parse ascii digits as number"); 90 | (offset, Some(TokenKind::Number(num))) 91 | } 92 | c if c.is_whitespace() => (c.len_utf8(), None), 93 | '=' => ('='.len_utf8(), Some(TokenKind::Equal)), 94 | '(' => ('('.len_utf8(), Some(TokenKind::OpenParen)), 95 | ')' => (')'.len_utf8(), Some(TokenKind::ClosedParen)), 96 | '/' => ('/'.len_utf8(), Some(TokenKind::Slash)), 97 | '#' => ('/'.len_utf8(), Some(TokenKind::Hash)), 98 | ':' => ('/'.len_utf8(), Some(TokenKind::Colon)), 99 | '[' => ('['.len_utf8(), Some(TokenKind::OpenBracket)), 100 | ']' => (']'.len_utf8(), Some(TokenKind::ClosedBracket)), 101 | ',' => (','.len_utf8(), Some(TokenKind::Comma)), 102 | '.' => { 103 | if matches!(chars.peek(), Some(c) if c.is_alphabetic()) { 104 | let len: usize = chars 105 | .take_while(|c| c.is_ascii_alphabetic() || *c == '_') 106 | .map(|c| c.len_utf8()) 107 | .sum(); 108 | let offset = '.'.len_utf8() + len; 109 | let ident = &rest.str[1..offset]; 110 | (offset, Some(TokenKind::Builtin(ident))) 111 | } else { 112 | ('.'.len_utf8(), Some(TokenKind::Period)) 113 | } 114 | } 115 | '{' => ('.'.len_utf8(), Some(TokenKind::OpenBrace)), 116 | '}' => ('.'.len_utf8(), Some(TokenKind::ClosedBrace)), 117 | '-' if chars.peek() == Some(&'-') => { 118 | let len: usize = chars 119 | .skip(1) 120 | .take_while(|c| c.is_ascii_alphabetic() || *c == '_' || *c == '-') 121 | .map(|c| c.len_utf8()) 122 | .sum(); 123 | let offset = '-'.len_utf8() * 2 + len; 124 | let ident = &rest.str[2..offset]; 125 | (offset, Some(TokenKind::Flag(ident))) 126 | } 127 | _ => return Err(TokenizeError::UnexpectedChar(first, original_offset)), 128 | }; 129 | Ok(( 130 | rest.offset(offset), 131 | token_kind.map(|token| Token { 132 | input: SpannedStr { 133 | str: &rest.str[..offset], 134 | offset: original_offset, 135 | }, 136 | token, 137 | }), 138 | )) 139 | } 140 | } 141 | 142 | #[derive(Debug, PartialEq)] 143 | pub enum TokenizeError { 144 | UnexpectedChar(char, usize), 145 | } 146 | 147 | impl std::error::Error for TokenizeError {} 148 | 149 | impl std::fmt::Display for TokenizeError { 150 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 151 | match self { 152 | TokenizeError::UnexpectedChar(char, _) => { 153 | f.write_str("unexpected character: ")?; 154 | f.write_char(*char) 155 | } 156 | } 157 | } 158 | } 159 | 160 | /// A view into the input str with span information 161 | #[derive(Debug, PartialEq, Clone, Copy)] 162 | pub struct SpannedStr<'a> { 163 | pub str: &'a str, 164 | pub offset: usize, 165 | } 166 | 167 | impl<'a> SpannedStr<'a> { 168 | fn offset(self, offset: usize) -> SpannedStr<'a> { 169 | Self { 170 | str: &self.str[offset..], 171 | offset: self.offset + offset, 172 | } 173 | } 174 | } 175 | 176 | impl Deref for SpannedStr<'_> { 177 | type Target = str; 178 | 179 | fn deref(&self) -> &Self::Target { 180 | self.str 181 | } 182 | } 183 | 184 | #[cfg(test)] 185 | mod tests { 186 | pub use super::*; 187 | #[test] 188 | fn tokenize_literals() { 189 | let input = r#" "hello-world" "#; 190 | let tokens = Token::tokenize(input).unwrap(); 191 | assert_eq!(tokens.len(), 1); 192 | assert_eq!( 193 | tokens[0], 194 | Token { 195 | input: SpannedStr { 196 | str: r#""hello-world""#, 197 | offset: 2 198 | }, 199 | token: TokenKind::String("hello-world") 200 | } 201 | ); 202 | 203 | let input = " hello "; 204 | let tokens = Token::tokenize(input).unwrap(); 205 | assert_eq!(tokens.len(), 1); 206 | assert_eq!( 207 | tokens[0], 208 | Token { 209 | input: SpannedStr { 210 | str: "hello", 211 | offset: 2 212 | }, 213 | token: TokenKind::Ident("hello") 214 | } 215 | ); 216 | } 217 | 218 | #[test] 219 | fn tokenize_ident() { 220 | let input = " hello- "; 221 | let err = Token::tokenize(input).unwrap_err(); 222 | assert_eq!(err, TokenizeError::UnexpectedChar('-', 7)) 223 | } 224 | 225 | #[test] 226 | fn tokenize_assignment() { 227 | let input = r#" hello = "world" "#; 228 | let tokens = Token::tokenize(input) 229 | .unwrap() 230 | .into_iter() 231 | .map(|t| t.token) 232 | .collect::>(); 233 | assert_eq!( 234 | tokens, 235 | vec![ 236 | TokenKind::Ident("hello"), 237 | TokenKind::Equal, 238 | TokenKind::String("world"), 239 | ] 240 | ) 241 | } 242 | 243 | #[test] 244 | fn tokenize_builtin() { 245 | let input = ".foo hello"; 246 | let tokens = Token::tokenize(input) 247 | .unwrap() 248 | .into_iter() 249 | .map(|t| t.token) 250 | .collect::>(); 251 | assert_eq!( 252 | tokens, 253 | vec![TokenKind::Builtin("foo"), TokenKind::Ident("hello"),] 254 | ) 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/evaluator.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use anyhow::{bail, Context}; 4 | use wasmtime::component::{self, Val}; 5 | 6 | use crate::{command::parser, runtime::Runtime, wit::WorldResolver}; 7 | 8 | pub struct Evaluator<'a> { 9 | runtime: &'a mut Runtime, 10 | resolver: &'a WorldResolver, 11 | scope: &'a HashMap, 12 | } 13 | 14 | impl<'a> Evaluator<'a> { 15 | /// Create a new evaluator 16 | pub fn new( 17 | runtime: &'a mut Runtime, 18 | resolver: &'a WorldResolver, 19 | scope: &'a HashMap, 20 | ) -> Self { 21 | Self { 22 | runtime, 23 | resolver, 24 | scope, 25 | } 26 | } 27 | 28 | /// Evaluate the expression with the provided type hint 29 | pub fn eval( 30 | &mut self, 31 | expr: parser::Expr<'_>, 32 | type_hint: Option<&component::Type>, 33 | ) -> anyhow::Result { 34 | match expr { 35 | parser::Expr::Literal(l) => self.eval_literal(l, type_hint), 36 | parser::Expr::Ident(ident) => self.resolve_ident(ident, type_hint), 37 | parser::Expr::FunctionCall(func) => { 38 | let ident = func.ident; 39 | let mut args = func.args; 40 | log::debug!( 41 | "Checking for type constructor for {ident} #args={} type_hint={type_hint:?}", 42 | args.len() 43 | ); 44 | // If the preferred type has some sort of type constructor, try that first 45 | match type_hint { 46 | Some(component::Type::Option(o)) 47 | if ident.interface.is_none() && ident.item == "some" && args.len() == 1 => 48 | { 49 | let val = self.eval(args.remove(0), Some(&o.ty()))?; 50 | return Ok(val); 51 | } 52 | Some(component::Type::Result(r)) if args.len() == 1 => { 53 | if let Some(ok) = r.ok() { 54 | if ident.interface.is_none() && ident.item == "ok" { 55 | let val = self.eval(args.remove(0), Some(&ok))?; 56 | return Ok(val); 57 | } 58 | } 59 | if let Some(err) = r.err() { 60 | if ident.interface.is_none() && ident.item == "err" { 61 | let val = self.eval(args.remove(0), Some(&err))?; 62 | return Ok(val); 63 | } 64 | } 65 | } 66 | _ => {} 67 | } 68 | 69 | let mut results = self.call_func(ident, args)?; 70 | if results.len() != 1 { 71 | bail!( 72 | "Expected function '{ident}' to return one result but got {}", 73 | results.len() 74 | ) 75 | } 76 | Ok(results.remove(0)) 77 | } 78 | } 79 | } 80 | 81 | /// Call the function 82 | pub fn call_func( 83 | &mut self, 84 | ident: parser::ItemIdent, 85 | args: Vec>, 86 | ) -> anyhow::Result> { 87 | log::debug!("Calling function: {ident} with args: {args:?}"); 88 | let func_def = self 89 | .resolver 90 | .exported_function(ident) 91 | .with_context(|| format!("no function with name '{ident}'"))?; 92 | let mut evaled_args = Vec::with_capacity(func_def.params.len()); 93 | if func_def.params.len() != args.len() { 94 | bail!( 95 | "tried to call a function that has {} params with {} args", 96 | func_def.params.len(), 97 | args.len() 98 | ) 99 | } 100 | let func = self.runtime.get_func(ident)?; 101 | let names = func_def.params.iter().map(|(n, _)| n); 102 | let types = func.params(&mut self.runtime.store); 103 | for (param_name, (param_type, arg)) in names.zip(types.iter().zip(args)) { 104 | let evaled_arg = self 105 | .eval(arg, Some(param_type)) 106 | .map_err(|e| anyhow::anyhow!("argument '{param_name}': {e}"))?; 107 | evaled_args.push(evaled_arg); 108 | } 109 | let results = self 110 | .runtime 111 | .call_func(func, &evaled_args, func_def.results.len())?; 112 | Ok(results) 113 | } 114 | 115 | /// Evaluate a literal using the provided type hint 116 | pub fn eval_literal( 117 | &mut self, 118 | literal: parser::Literal<'_>, 119 | type_hint: Option<&component::Type>, 120 | ) -> anyhow::Result { 121 | match literal { 122 | parser::Literal::List(list) => { 123 | match type_hint { 124 | Some(component::Type::List(l)) => { 125 | let mut values = Vec::new(); 126 | for item in list.items { 127 | values.push(self.eval(item, Some(&l.ty()))?) 128 | } 129 | Ok(Val::List(values)) 130 | } 131 | Some(component::Type::Option(o)) => match o.ty() { 132 | component::Type::List(l) => { 133 | let mut values = Vec::new(); 134 | for item in list.items { 135 | values.push(self.eval(item, Some(&l.ty()))?) 136 | } 137 | Ok(Val::Option(Some(Box::new(Val::List(values))))) 138 | } 139 | t => bail!( 140 | "type error - required option<{}> found = list", 141 | display_component_type(&t) 142 | ), 143 | }, 144 | Some(t) => bail!( 145 | "type error - required = {} found = list", 146 | display_component_type(t) 147 | ), 148 | None => { 149 | // TODO: try to find a list type that fits the shape of the literal 150 | bail!("cannot determine type of list") 151 | } 152 | } 153 | } 154 | parser::Literal::Record(mut r) => { 155 | let ty = match type_hint { 156 | Some(component::Type::Record(r)) => r, 157 | Some(t) => bail!( 158 | "type error - required = {} found = record", 159 | display_component_type(t) 160 | ), 161 | None => { 162 | // TODO: try to find a record type that fits the shape of the literal 163 | bail!("cannot determine type of record") 164 | } 165 | }; 166 | let mut values = Vec::new(); 167 | let types = ty 168 | .fields() 169 | .enumerate() 170 | .map(|(index, field)| (field.name, index)) 171 | .collect::>(); 172 | // Sort the fields since wasmtime expects the fields to be in the defined order 173 | r.fields 174 | .sort_by(|(f1, _), (f2, _)| types.get(f1).unwrap().cmp(types.get(f2).unwrap())); 175 | 176 | for ((name, field_expr), field_type) in r.fields.into_iter().zip(ty.fields()) { 177 | values.push(( 178 | name.to_owned(), 179 | self.eval(field_expr, Some(&field_type.ty))?, 180 | )); 181 | } 182 | Ok(Val::Record(values)) 183 | } 184 | parser::Literal::String(s) => { 185 | let val = Val::String(s.to_owned()); 186 | match type_hint { 187 | Some(component::Type::Result(r)) => Ok(Val::Result(match (r.ok(), r.err()) { 188 | (Some(_), _) => Ok(Some(Box::new(val))), 189 | (_, Some(_)) => Err(Some(Box::new(val))), 190 | (None, None) => return Ok(val), 191 | })), 192 | _ => Ok(val), 193 | } 194 | } 195 | parser::Literal::Number(n) => match type_hint { 196 | Some(component::Type::U8) => Ok(Val::U8(n.try_into()?)), 197 | _ => Ok(Val::S32(n.try_into()?)), 198 | }, 199 | } 200 | } 201 | 202 | fn resolve_ident( 203 | &mut self, 204 | ident: &str, 205 | type_hint: Option<&component::Type>, 206 | ) -> Result { 207 | log::debug!("Resolving ident {ident} with type hint {type_hint:?}"); 208 | match type_hint { 209 | Some(t) => match t { 210 | component::Type::Bool if ident == "true" => Ok(Val::Bool(true)), 211 | component::Type::Bool if ident == "false" => Ok(Val::Bool(false)), 212 | component::Type::Enum(_) => Ok(Val::Enum(ident.to_owned())), 213 | component::Type::Variant(_) => match self.lookup_in_scope(ident) { 214 | Ok(v) => Ok(v), 215 | Err(_) => Ok(Val::Option(None)), 216 | }, 217 | component::Type::Option(_) if ident == "none" => Ok(Val::Option(None)), 218 | component::Type::Option(o) => Ok(Val::Option(Some(Box::new( 219 | self.resolve_ident(ident, Some(&o.ty()))?, 220 | )))), 221 | component::Type::Result(r) => Ok(match (r.ok(), r.err()) { 222 | (Some(o), _) => { 223 | Val::Result(Ok(Some(Box::new(self.resolve_ident(ident, Some(&o))?)))) 224 | } 225 | (None, None) if ident == "ok" => Val::Result(Ok(None)), 226 | (None, None) if ident == "err" => Val::Result(Err(None)), 227 | _ => return self.lookup_in_scope(ident), 228 | }), 229 | component::Type::Bool 230 | | component::Type::U8 231 | | component::Type::U16 232 | | component::Type::U32 233 | | component::Type::U64 234 | | component::Type::S8 235 | | component::Type::S16 236 | | component::Type::S32 237 | | component::Type::S64 238 | | component::Type::String => self.lookup_in_scope(ident), 239 | t => todo!("handle ident '{ident}' with type {t:?}"), 240 | }, 241 | None => self.lookup_in_scope(ident), 242 | } 243 | } 244 | 245 | fn lookup_in_scope(&self, ident: &str) -> anyhow::Result { 246 | self.scope 247 | .get(ident) 248 | .with_context(|| format!("no identifier '{ident}' in scope")) 249 | .cloned() 250 | } 251 | } 252 | 253 | fn display_component_type(ty: &component::Type) -> &'static str { 254 | match ty { 255 | component::Type::Bool => "bool", 256 | component::Type::S8 => "s8", 257 | component::Type::U8 => "u8", 258 | component::Type::S16 => "s16", 259 | component::Type::U16 => "u16", 260 | component::Type::S32 => "s32", 261 | component::Type::U32 => "u32", 262 | component::Type::S64 => "s64", 263 | component::Type::U64 => "u64", 264 | component::Type::Float32 => "float32", 265 | component::Type::Float64 => "float64", 266 | component::Type::Char => "char", 267 | component::Type::String => "string", 268 | component::Type::List(_) => "list", 269 | component::Type::Record(_) => "record", 270 | component::Type::Tuple(_) => "tuple", 271 | component::Type::Variant(_) => "variant", 272 | component::Type::Enum(_) => "enum", 273 | component::Type::Option(_) => "option", 274 | component::Type::Result(_) => "result", 275 | component::Type::Flags(_) => "flags", 276 | component::Type::Own(_) => "own", 277 | component::Type::Borrow(_) => "borrow", 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod command; 2 | mod evaluator; 3 | mod runtime; 4 | mod wit; 5 | 6 | use std::collections::HashMap; 7 | 8 | use anyhow::Context as _; 9 | use clap::Parser; 10 | use colored::*; 11 | use rustyline::error::ReadlineError; 12 | 13 | fn main() { 14 | if let Err(e) = _main() { 15 | print_error_prefix(); 16 | eprintln!("{e}"); 17 | if e.source().is_some() { 18 | eprintln!("\nCaused by:"); 19 | } 20 | for e in e.chain().skip(1) { 21 | eprintln!(" {e}") 22 | } 23 | } 24 | } 25 | 26 | fn _main() -> anyhow::Result<()> { 27 | env_logger::init(); 28 | 29 | let cli = Cli::parse(); 30 | let component_bytes = std::fs::read(cli.component)?; 31 | let mut resolver = wit::WorldResolver::from_bytes(&component_bytes)?; 32 | let mut runtime = runtime::Runtime::init(component_bytes, &resolver, |import_name| { 33 | print_error_prefix(); 34 | eprintln!("unimplemented import: {import_name}"); 35 | })?; 36 | 37 | let mut rl = rustyline::DefaultEditor::new()?; 38 | if let Some(home) = home::home_dir() { 39 | let _ = rl.load_history(&home.join(".weplhistory")); 40 | } 41 | let world = resolver.world_name(); 42 | println!("{}: {world}", "World".blue().bold()); 43 | let mut scope = HashMap::default(); 44 | let prompt = "> ".blue().bold().to_string(); 45 | loop { 46 | let readline = rl.readline(&prompt); 47 | match readline { 48 | Ok(line) => { 49 | let _ = rl.add_history_entry(&line); 50 | let line = command::Cmd::parse(&line); 51 | match line { 52 | Ok(Some(cmd)) => { 53 | match cmd.run(&mut runtime, &mut resolver, &mut scope) { 54 | Err(e) => { 55 | print_error_prefix(); 56 | eprintln!("{e}"); 57 | // Refresh the runtime on error so we start fresh 58 | runtime.refresh().context("error refreshing wasm runtime")?; 59 | } 60 | Ok(true) => { 61 | let _ = rl.clear_screen(); 62 | } 63 | _ => {} 64 | } 65 | } 66 | Ok(None) => continue, 67 | Err(e) => { 68 | print_error_prefix(); 69 | eprintln!("{e}") 70 | } 71 | } 72 | } 73 | Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break, 74 | Err(ReadlineError::WindowResized) => continue, 75 | Err(ReadlineError::Io(e)) => { 76 | print_error_prefix(); 77 | eprintln!("reading from stdin failed: {e}"); 78 | break; 79 | } 80 | Err(e) => { 81 | print_error_prefix(); 82 | eprintln!("reading from stdin failed: {e}"); 83 | break; 84 | } 85 | } 86 | } 87 | if let Some(home) = home::home_dir() { 88 | let _ = rl.save_history(&home.join(".weplhistory")); 89 | } 90 | 91 | Ok(()) 92 | } 93 | 94 | fn print_error_prefix() { 95 | print_prefix("Error: ", colored::Color::Red) 96 | } 97 | 98 | fn print_prefix(prefix: &str, color: colored::Color) { 99 | use std::io::Write; 100 | let mut stderr = std::io::stderr(); 101 | let _ = write!(&mut stderr, "{}", prefix.color(color).bold()); 102 | let _ = stderr.flush(); 103 | } 104 | 105 | /// The WebAssembly Component repl. 106 | #[derive(Parser, Debug)] 107 | #[command(author, version, about, long_about = None)] 108 | struct Cli { 109 | /// Path to component binary 110 | component: std::path::PathBuf, 111 | } 112 | -------------------------------------------------------------------------------- /src/runtime.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use anyhow::Context as _; 7 | use colored::Colorize; 8 | use wasmtime::{ 9 | component::{Component, Func, Instance, Linker, ResourceTable, Val}, 10 | Config, Engine, Store, 11 | }; 12 | use wasmtime_wasi::{ 13 | HostOutputStream, Stdout, StdoutStream, StreamResult, Subscribe, WasiCtx, WasiCtxBuilder, 14 | WasiView, 15 | }; 16 | 17 | use crate::{ 18 | command::parser::{self, ItemIdent}, 19 | wit::WorldResolver, 20 | }; 21 | 22 | pub struct Runtime { 23 | engine: Engine, 24 | pub store: Store, 25 | instance: Instance, 26 | linker: Linker, 27 | component: (Component, Vec), 28 | import_impls: ImportImpls, 29 | } 30 | 31 | impl Runtime { 32 | pub fn init( 33 | component_bytes: Vec, 34 | resolver: &WorldResolver, 35 | stub_import: impl Fn(&str) + Sync + Send + Clone + 'static, 36 | ) -> anyhow::Result { 37 | let engine = load_engine()?; 38 | let component = load_component(&engine, &component_bytes)?; 39 | let mut linker = Linker::::new(&engine); 40 | linker.allow_shadowing(true); 41 | 42 | let imports_wasi_cli = resolver.imports_wasi_cli(); 43 | if imports_wasi_cli { 44 | log::debug!("Linking with wasi"); 45 | wasmtime_wasi::add_to_linker_sync(&mut linker)?; 46 | } 47 | for (import_name, import) in resolver.imports(!imports_wasi_cli) { 48 | let import_name = resolver.world_item_name(import_name); 49 | let stub_import = stub_import.clone(); 50 | match import { 51 | wit_parser::WorldItem::Function(f) => { 52 | linker.root().func_new(&f.name, move |_ctx, _args, _rets| { 53 | stub_import(&import_name); 54 | Ok(()) 55 | })?; 56 | } 57 | wit_parser::WorldItem::Interface { id, .. } => { 58 | let interface = resolver.interface_by_id(*id).unwrap(); 59 | let mut root = linker.root(); 60 | let mut instance = root.instance(&import_name)?; 61 | for (_, f) in interface.functions.iter() { 62 | let stub_import = stub_import.clone(); 63 | let import_name = import_name.clone(); 64 | instance.func_new(&f.name, move |_ctx, _args, _rets| { 65 | stub_import(&import_name); 66 | Ok(()) 67 | })?; 68 | } 69 | for (name, t) in &interface.types { 70 | let t = resolver.type_by_id(*t).unwrap(); 71 | if let wit_parser::TypeDefKind::Resource = &t.kind { 72 | let ty = wasmtime::component::ResourceType::host::<()>(); 73 | instance.resource(name, ty, |_, _| Ok(())).unwrap(); 74 | } 75 | } 76 | } 77 | _ => {} 78 | } 79 | } 80 | let pre = linker 81 | .instantiate_pre(&component) 82 | .context("could not instantiate component")?; 83 | let mut store = build_store(&engine); 84 | let instance = pre.instantiate(&mut store)?; 85 | let import_impls = ImportImpls::new(&engine, String::from("import")); 86 | Ok(Self { 87 | engine, 88 | store, 89 | instance, 90 | linker, 91 | component: (component, component_bytes), 92 | import_impls, 93 | }) 94 | } 95 | 96 | pub fn get_func(&mut self, ident: ItemIdent) -> anyhow::Result { 97 | let func = match ident.interface { 98 | Some(i) => { 99 | let mut exports = self.instance.exports(&mut self.store); 100 | let instance_name = i.to_string(); 101 | exports 102 | .instance(&instance_name) 103 | .with_context(|| { 104 | format!("could not find exported instance with name '{instance_name}'") 105 | })? 106 | .func(ident.item) 107 | } 108 | None => self 109 | .instance 110 | .exports(&mut self.store) 111 | .root() 112 | .func(ident.item), 113 | }; 114 | func.with_context(|| format!("could not find function '{ident}' in instance")) 115 | } 116 | 117 | pub fn call_func( 118 | &mut self, 119 | func: Func, 120 | args: &[Val], 121 | result_count: usize, 122 | ) -> anyhow::Result> { 123 | let mut results = vec![Val::Bool(Default::default()); result_count]; 124 | func.call(&mut self.store, args, &mut results)?; 125 | func.post_return(&mut self.store)?; 126 | Ok(results) 127 | } 128 | 129 | /// Stub a function with an export from the component encoded in `component_bytes` 130 | /// 131 | /// This function does not check that the component in `components_bytes` has the 132 | /// export needed. 133 | pub fn stub( 134 | &mut self, 135 | resolver: &WorldResolver, 136 | import_ident: parser::Ident<'_>, 137 | export_ident: parser::Ident<'_>, 138 | component_bytes: &[u8], 139 | ) -> anyhow::Result<()> { 140 | match (import_ident, export_ident) { 141 | (parser::Ident::Item(import_ident), parser::Ident::Item(export_ident)) => { 142 | self.stub_function(resolver, import_ident, export_ident, component_bytes) 143 | } 144 | (parser::Ident::Interface(import_ident), parser::Ident::Interface(export_ident)) => { 145 | self.stub_interface(resolver, import_ident, export_ident, component_bytes) 146 | } 147 | (parser::Ident::Interface(_), parser::Ident::Item(_)) => { 148 | anyhow::bail!("cannot satisfy interface import with a function") 149 | } 150 | (parser::Ident::Item(_), parser::Ident::Interface(_)) => { 151 | anyhow::bail!("cannot satisfy function import with an interface") 152 | } 153 | } 154 | } 155 | 156 | pub fn stub_interface( 157 | &mut self, 158 | resolver: &WorldResolver, 159 | import_ident: parser::InterfaceIdent<'_>, 160 | export_ident: parser::InterfaceIdent<'_>, 161 | component_bytes: &[u8], 162 | ) -> anyhow::Result<()> { 163 | let component = load_component(&self.engine, component_bytes)?; 164 | let mut linker = Linker::::new(&self.engine); 165 | wasmtime_wasi::add_to_linker_sync(&mut linker)?; 166 | let mut root = self.linker.root(); 167 | let mut import_instance = root 168 | .instance(&import_ident.to_string()) 169 | .with_context(|| format!("no imported instance named '{import_ident}' found"))?; 170 | let import = resolver 171 | .imported_interface(import_ident) 172 | .with_context(|| format!("no imported interface named '{import_ident}' found"))?; 173 | let other = WorldResolver::from_bytes(component_bytes)?; 174 | let export = other 175 | .exported_interface(export_ident) 176 | .with_context(|| format!("no exported interface named '{export_ident}' found"))?; 177 | { 178 | let mut store_lock = self.import_impls.store.lock().unwrap(); 179 | let export_instance = linker.instantiate(&mut *store_lock, &component)?; 180 | for (fun_name, imported_function) in &import.functions { 181 | let exported_function = export 182 | .functions 183 | .get(fun_name) 184 | .with_context(|| format!("no exported function named '{fun_name}' found"))?; 185 | if imported_function.params.len() != exported_function.params.len() { 186 | anyhow::bail!("different number of parameters") 187 | } 188 | for ((arg_name, p1), (_, p2)) in imported_function 189 | .params 190 | .iter() 191 | .zip(&exported_function.params) 192 | { 193 | if !types_equal(resolver, p1, &other, p2) { 194 | anyhow::bail!( 195 | "different types for arg '{arg_name}' in function '{fun_name}'" 196 | ) 197 | } 198 | } 199 | match (&imported_function.results, &exported_function.results) { 200 | (wit_parser::Results::Named(is), wit_parser::Results::Named(es)) => { 201 | if is.len() != es.len() { 202 | anyhow::bail!("different number of return types") 203 | } 204 | let es = es 205 | .iter() 206 | .map(|(name, ty)| (name, ty)) 207 | .collect::>(); 208 | for (name, ty) in is { 209 | let e = es.get(name).with_context(|| format!("exported function '{fun_name}' does not have return value '{name}'"))?; 210 | if !types_equal(resolver, ty, &other, e) { 211 | anyhow::bail!("return value '{name}' has differing types"); 212 | } 213 | } 214 | } 215 | (wit_parser::Results::Anon(t1), wit_parser::Results::Anon(t2)) => { 216 | if !types_equal(resolver, t1, &other, t2) { 217 | anyhow::bail!("return types did not match for function {fun_name}"); 218 | } 219 | } 220 | _ => anyhow::bail!("different return type kinds for function '{fun_name}'"), 221 | } 222 | let store = self.import_impls.store.clone(); 223 | 224 | let export_func = { 225 | let mut exports = export_instance.exports(&mut *store_lock); 226 | let mut export_instance = exports 227 | .instance(&export_ident.to_string()) 228 | .with_context(|| { 229 | format!("no exported instance named '{export_ident} found'") 230 | })?; 231 | export_instance 232 | .func(fun_name) 233 | .with_context(|| format!("no exported function named '{fun_name}' found"))? 234 | }; 235 | import_instance.func_new(fun_name, move |_ctx, args, results| { 236 | let mut store = store.lock().unwrap(); 237 | export_func.call(&mut *store, args, results)?; 238 | export_func.post_return(&mut *store)?; 239 | Ok(()) 240 | })?; 241 | } 242 | } 243 | self.refresh()?; 244 | Ok(()) 245 | } 246 | 247 | pub fn stub_function( 248 | &mut self, 249 | resolver: &WorldResolver, 250 | import_ident: parser::ItemIdent<'_>, 251 | export_ident: parser::ItemIdent<'_>, 252 | component_bytes: &[u8], 253 | ) -> anyhow::Result<()> { 254 | // type checking 255 | let import = resolver 256 | .imported_function(import_ident) 257 | .with_context(|| format!("no import with name '{import_ident}'"))?; 258 | let other = WorldResolver::from_bytes(component_bytes)?; 259 | let export = other 260 | .exported_function(export_ident) 261 | .with_context(|| format!("no export with name '{export_ident}'"))?; 262 | if import.params != export.params { 263 | anyhow::bail!("params not equal") 264 | } 265 | if import.results != export.results { 266 | anyhow::bail!("return values not equal") 267 | } 268 | 269 | let component = load_component(&self.engine, component_bytes)?; 270 | let mut linker = Linker::::new(&self.engine); 271 | wasmtime_wasi::add_to_linker_sync(&mut linker)?; 272 | let export_func = { 273 | let mut store_lock = self.import_impls.store.lock().unwrap(); 274 | let export_instance = linker.instantiate(&mut *store_lock, &component)?; 275 | match export_ident.interface { 276 | Some(interface) => { 277 | let mut export = export_instance.exports(&mut *store_lock); 278 | let mut instance = export 279 | .instance(&interface.to_string()) 280 | .with_context(|| format!("no export named '{interface} found'"))?; 281 | instance.func(export_ident.item) 282 | } 283 | None => export_instance.get_func(&mut *store_lock, export_ident.item), 284 | } 285 | } 286 | .with_context(|| format!("no function found named '{export_ident}'"))?; 287 | 288 | let store = self.import_impls.store.clone(); 289 | let name = import_ident.item.to_owned(); 290 | match import_ident.interface { 291 | Some(interface) => { 292 | let mut instance = self 293 | .linker 294 | .instance(&interface.to_string()) 295 | .with_context(|| format!("no interface named '{interface}' found"))?; 296 | instance.func_new(&name, move |_ctx, args, results| { 297 | let mut store = store.lock().unwrap(); 298 | export_func.call(&mut *store, args, results)?; 299 | export_func.post_return(&mut *store)?; 300 | Ok(()) 301 | })?; 302 | } 303 | None => { 304 | self.linker 305 | .root() 306 | .func_new(&name, move |_ctx, args, results| { 307 | let mut store = store.lock().unwrap(); 308 | export_func.call(&mut *store, args, results)?; 309 | export_func.post_return(&mut *store)?; 310 | Ok(()) 311 | })?; 312 | } 313 | } 314 | self.refresh()?; 315 | Ok(()) 316 | } 317 | 318 | pub fn set_component(&mut self, component: Vec) -> anyhow::Result<()> { 319 | self.component = (Component::from_binary(&self.engine, &component)?, component); 320 | self.refresh() 321 | } 322 | 323 | pub fn compose(&mut self, adapter: &[u8]) -> Result<(), anyhow::Error> { 324 | let temp = std::env::temp_dir(); 325 | let tmp_virt = temp.join("virt.wasm"); 326 | std::fs::write(&tmp_virt, adapter)?; 327 | let tmp_component = temp.join("component.wasm"); 328 | std::fs::write(&tmp_component, &self.component.1)?; 329 | 330 | let bytes = wasm_compose::composer::ComponentComposer::new( 331 | &tmp_component, 332 | &wasm_compose::config::Config { 333 | definitions: vec![tmp_virt], 334 | ..Default::default() 335 | }, 336 | ) 337 | .compose()?; 338 | self.set_component(bytes) 339 | } 340 | 341 | pub fn component_bytes(&self) -> &[u8] { 342 | &self.component.1 343 | } 344 | 345 | /// Get a new instance 346 | pub fn refresh(&mut self) -> anyhow::Result<()> { 347 | self.store = build_store(&self.engine); 348 | self.instance = self 349 | .linker 350 | .instantiate(&mut self.store, &self.component.0)?; 351 | Ok(()) 352 | } 353 | } 354 | 355 | /// A collection of instances that implement the main components imports 356 | struct ImportImpls { 357 | store: Arc>>, 358 | } 359 | 360 | impl ImportImpls { 361 | fn new(engine: &Engine, prefix: String) -> Self { 362 | let table = ResourceTable::new(); 363 | let mut builder = WasiCtxBuilder::new(); 364 | builder.inherit_stderr(); 365 | builder.stdout(ImportImplStdout::new(prefix)); 366 | let wasi = builder.build(); 367 | let context = ImportImplsContext::new(table, wasi); 368 | let store = Store::new(engine, context); 369 | 370 | Self { 371 | store: Arc::new(Mutex::new(store)), 372 | } 373 | } 374 | } 375 | 376 | struct ImportImplStdout { 377 | stream: Box, 378 | prefix: String, 379 | } 380 | 381 | impl ImportImplStdout { 382 | fn new(prefix: String) -> Self { 383 | let prefix = format!("<{}>", prefix).green().bold(); 384 | let stream = Stdout.stream(); 385 | Self { 386 | stream, 387 | prefix: prefix.to_string(), 388 | } 389 | } 390 | } 391 | 392 | #[async_trait::async_trait] 393 | impl HostOutputStream for ImportImplStdout { 394 | fn write(&mut self, bytes: bytes::Bytes) -> StreamResult<()> { 395 | let output = String::from_utf8_lossy(&bytes); 396 | let output = format!("{} {output}", self.prefix); 397 | self.stream.write(output.into_bytes().into()) 398 | } 399 | 400 | fn flush(&mut self) -> StreamResult<()> { 401 | self.stream.flush() 402 | } 403 | 404 | fn check_write(&mut self) -> StreamResult { 405 | self.stream.check_write() 406 | } 407 | 408 | async fn write_ready(&mut self) -> StreamResult { 409 | self.stream.write_ready().await 410 | } 411 | } 412 | 413 | #[async_trait::async_trait] 414 | impl Subscribe for ImportImplStdout { 415 | async fn ready(&mut self) { 416 | self.stream.ready().await 417 | } 418 | } 419 | 420 | #[async_trait::async_trait] 421 | impl StdoutStream for ImportImplStdout { 422 | fn stream(&self) -> Box<(dyn HostOutputStream + 'static)> { 423 | Stdout.stream() 424 | } 425 | 426 | fn isatty(&self) -> bool { 427 | Stdout.isatty() 428 | } 429 | } 430 | 431 | fn build_store(engine: &Engine) -> Store { 432 | let table = ResourceTable::new(); 433 | let mut builder = WasiCtxBuilder::new(); 434 | builder.inherit_stdout().inherit_stderr(); 435 | let wasi = builder.build(); 436 | let context = Context::new(table, wasi); 437 | Store::new(engine, context) 438 | } 439 | 440 | pub struct Context { 441 | table: ResourceTable, 442 | wasi: WasiCtx, 443 | } 444 | 445 | impl Context { 446 | fn new(table: ResourceTable, wasi: WasiCtx) -> Self { 447 | Self { table, wasi } 448 | } 449 | } 450 | 451 | impl WasiView for Context { 452 | fn table(&mut self) -> &mut ResourceTable { 453 | &mut self.table 454 | } 455 | 456 | fn ctx(&mut self) -> &mut WasiCtx { 457 | &mut self.wasi 458 | } 459 | } 460 | 461 | fn load_engine() -> anyhow::Result { 462 | let mut config = Config::new(); 463 | config.wasm_component_model(true); 464 | 465 | Engine::new(&config) 466 | } 467 | 468 | fn load_component(engine: &Engine, component_bytes: &[u8]) -> anyhow::Result { 469 | Component::new(engine, component_bytes) 470 | } 471 | 472 | struct ImportImplsContext { 473 | table: ResourceTable, 474 | wasi: WasiCtx, 475 | } 476 | 477 | impl ImportImplsContext { 478 | fn new(table: ResourceTable, wasi: WasiCtx) -> Self { 479 | Self { table, wasi } 480 | } 481 | } 482 | 483 | impl WasiView for ImportImplsContext { 484 | fn table(&mut self) -> &mut ResourceTable { 485 | &mut self.table 486 | } 487 | 488 | fn ctx(&mut self) -> &mut WasiCtx { 489 | &mut self.wasi 490 | } 491 | } 492 | 493 | fn types_equal( 494 | resolver1: &WorldResolver, 495 | t1: &wit_parser::Type, 496 | resolver2: &WorldResolver, 497 | t2: &wit_parser::Type, 498 | ) -> bool { 499 | match (t1, t2) { 500 | (wit_parser::Type::Id(t1), wit_parser::Type::Id(t2)) => { 501 | let t1 = resolver1.type_by_id(*t1).unwrap(); 502 | let t2 = resolver2.type_by_id(*t2).unwrap(); 503 | type_defs_equal(resolver1, &t1.kind, resolver2, &t2.kind) 504 | } 505 | (wit_parser::Type::Id(t1), t2) => { 506 | let t1 = resolver1.type_by_id(*t1).unwrap(); 507 | if let wit_parser::TypeDefKind::Type(t1) = &t1.kind { 508 | types_equal(resolver1, t1, resolver2, t2) 509 | } else { 510 | false 511 | } 512 | } 513 | (t1, wit_parser::Type::Id(t2)) => { 514 | let t2 = resolver1.type_by_id(*t2).unwrap(); 515 | if let wit_parser::TypeDefKind::Type(t2) = &t2.kind { 516 | types_equal(resolver1, t1, resolver2, t2) 517 | } else { 518 | false 519 | } 520 | } 521 | (t1, t2) => t1 == t2, 522 | } 523 | } 524 | 525 | fn type_defs_equal( 526 | resolver1: &WorldResolver, 527 | t1: &wit_parser::TypeDefKind, 528 | resolver2: &WorldResolver, 529 | t2: &wit_parser::TypeDefKind, 530 | ) -> bool { 531 | match (t1, t2) { 532 | (wit_parser::TypeDefKind::Result(r1), wit_parser::TypeDefKind::Result(r2)) => { 533 | let oks = match (&r1.ok, &r2.ok) { 534 | (None, None) => true, 535 | (Some(t1), Some(t2)) => types_equal(resolver1, t1, resolver2, t2), 536 | _ => false, 537 | }; 538 | let errs = match (&r1.err, &r2.err) { 539 | (None, None) => true, 540 | (Some(t1), Some(t2)) => types_equal(resolver1, t1, resolver2, t2), 541 | _ => false, 542 | }; 543 | oks && errs 544 | } 545 | (wit_parser::TypeDefKind::List(t1), wit_parser::TypeDefKind::List(t2)) => { 546 | types_equal(resolver1, t1, resolver2, t2) 547 | } 548 | (wit_parser::TypeDefKind::Variant(v1), wit_parser::TypeDefKind::Variant(v2)) => { 549 | if v1.cases.len() != v2.cases.len() { 550 | return false; 551 | } 552 | v1.cases.iter().zip(v2.cases.iter()).all(|(c1, c2)| { 553 | let types_equal = match (&c1.ty, &c2.ty) { 554 | (Some(t1), Some(t2)) => types_equal(resolver1, t1, resolver2, t2), 555 | (None, None) => true, 556 | _ => false, 557 | }; 558 | c1.name == c2.name && types_equal 559 | }) 560 | } 561 | // TODO: more comparisons 562 | _ => false, 563 | } 564 | } 565 | -------------------------------------------------------------------------------- /src/wit.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use anyhow::Context; 4 | use wit_component::DecodedWasm; 5 | use wit_parser::{ 6 | Function, Interface, InterfaceId, Package, Resolve, TypeDef, TypeId, World, WorldId, WorldItem, 7 | WorldKey, 8 | }; 9 | 10 | use crate::command::parser; 11 | 12 | /// A resolver for a wit world. 13 | pub struct WorldResolver { 14 | resolve: Resolve, 15 | world_id: WorldId, 16 | } 17 | 18 | impl WorldResolver { 19 | /// Create new instance. 20 | /// 21 | /// Panics if the `world_id` is not found in the `resolve`. 22 | pub fn new(resolve: Resolve, world_id: WorldId) -> Self { 23 | let this = Self { resolve, world_id }; 24 | // Ensure the world can be resolved 25 | let _ = this.world(); 26 | this 27 | } 28 | 29 | /// Create a new instance from the given bytes. 30 | pub fn from_bytes(component_bytes: &[u8]) -> anyhow::Result { 31 | let (resolve, world) = match wit_component::decode(component_bytes) 32 | .context("could not decode given file as a WebAssembly component")? 33 | { 34 | DecodedWasm::Component(r, w) => (r, w), 35 | _ => anyhow::bail!("found wit package instead of the expect WebAssembly component"), 36 | }; 37 | Ok(Self::new(resolve, world)) 38 | } 39 | 40 | /// Get the exported function by the given `FunctionIdent`. 41 | pub fn exported_function(&self, ident: parser::ItemIdent) -> Option<&Function> { 42 | match ident.interface { 43 | Some(i) => { 44 | let interface = self.exported_interface(i)?; 45 | interface.functions.get(ident.item) 46 | } 47 | None => { 48 | if let WorldItem::Function(f) = &self.export(ident.item)? { 49 | Some(f) 50 | } else { 51 | None 52 | } 53 | } 54 | } 55 | } 56 | 57 | /// Get the imported function by the given `FunctionIdent`. 58 | pub fn imported_function(&self, ident: parser::ItemIdent) -> Option<&Function> { 59 | match ident.interface { 60 | Some(i) => { 61 | let interface = self.imported_interface(i)?; 62 | interface.functions.get(ident.item) 63 | } 64 | None => { 65 | if let WorldItem::Function(f) = &self.import(ident.item)? { 66 | Some(f) 67 | } else { 68 | None 69 | } 70 | } 71 | } 72 | } 73 | 74 | /// Get the exported interface by the given `InterfaceIdent`. 75 | pub fn exported_interface(&self, ident: parser::InterfaceIdent) -> Option<&Interface> { 76 | self.interface_in_items(ident, self.world().exports.iter()) 77 | } 78 | 79 | /// Get the imported interface by the given `InterfaceIdent`. 80 | pub fn imported_interface(&self, ident: parser::InterfaceIdent) -> Option<&Interface> { 81 | self.interface_in_items(ident, self.world().imports.iter()) 82 | } 83 | 84 | pub fn export(&self, name: &str) -> Option<&WorldItem> { 85 | self.get_world_item_by_name(self.world().exports.iter(), name) 86 | } 87 | 88 | pub fn import(&self, name: &str) -> Option<&WorldItem> { 89 | self.get_world_item_by_name(self.world().imports.iter(), name) 90 | } 91 | 92 | pub fn interface_by_id(&self, id: InterfaceId) -> Option<&wit_parser::Interface> { 93 | self.resolve.interfaces.get(id) 94 | } 95 | 96 | pub fn type_by_id(&self, id: TypeId) -> Option<&TypeDef> { 97 | self.resolve.types.get(id) 98 | } 99 | 100 | pub(crate) fn types_by_name(&self, name: &str) -> Vec<(Option<&InterfaceId>, &TypeDef)> { 101 | let mut types = Vec::new(); 102 | for (_, t) in &self.resolve.types { 103 | if t.name.as_deref().map(|n| n == name).unwrap_or_default() { 104 | let interface = if let wit_parser::TypeOwner::Interface(i) = &t.owner { 105 | Some(i) 106 | } else { 107 | None 108 | }; 109 | types.push((interface, t)); 110 | } 111 | } 112 | types 113 | } 114 | 115 | pub fn display_wit_type<'a>( 116 | &'a self, 117 | param_type: &wit_parser::Type, 118 | expansion: Expansion, 119 | ) -> Cow<'a, str> { 120 | let str = match param_type { 121 | wit_parser::Type::Bool => "bool", 122 | wit_parser::Type::U8 => "u8", 123 | wit_parser::Type::U16 => "u16", 124 | wit_parser::Type::U32 => "u32", 125 | wit_parser::Type::U64 => "u64", 126 | wit_parser::Type::S8 => "s8", 127 | wit_parser::Type::S16 => "s16", 128 | wit_parser::Type::S32 => "s32", 129 | wit_parser::Type::S64 => "s64", 130 | wit_parser::Type::F32 => "float32", 131 | wit_parser::Type::F64 => "float64", 132 | wit_parser::Type::String => "string", 133 | wit_parser::Type::Char => "char", 134 | wit_parser::Type::Id(id) => { 135 | let typ = self 136 | .resolve 137 | .types 138 | .get(*id) 139 | .expect("found type id for type not present in resolver"); 140 | return self.display_wit_type_def(typ, expansion); 141 | } 142 | }; 143 | Cow::Borrowed(str) 144 | } 145 | 146 | pub fn display_wit_type_def(&self, typ: &TypeDef, expansion: Expansion) -> Cow<'_, str> { 147 | let display = match &typ.kind { 148 | wit_parser::TypeDefKind::Option(o) => { 149 | format!("option<{}>", self.display_wit_type(o, Expansion::Collapsed)) 150 | } 151 | wit_parser::TypeDefKind::Result(r) => { 152 | let ok = 153 | r.ok.as_ref() 154 | .map(|o| self.display_wit_type(o, Expansion::Collapsed)); 155 | let err = r 156 | .err 157 | .as_ref() 158 | .map(|o| self.display_wit_type(o, Expansion::Collapsed)); 159 | match (ok, err) { 160 | (Some(ok), Some(err)) => format!("result<{ok}, {err}>"), 161 | (Some(t), _) => format!("result<{t}>"), 162 | (_, Some(t)) => format!("result<_, {t}>"), 163 | _ => "result".into(), 164 | } 165 | } 166 | wit_parser::TypeDefKind::Type(t) => return self.display_wit_type(t, expansion), 167 | wit_parser::TypeDefKind::List(t) => { 168 | format!("list<{}>", self.display_wit_type(t, Expansion::Collapsed)) 169 | } 170 | wit_parser::TypeDefKind::Tuple(t) => { 171 | format!( 172 | "tuple<{}>", 173 | t.types 174 | .iter() 175 | .map(|t| self.display_wit_type(t, Expansion::Collapsed)) 176 | .collect::>() 177 | .join(", ") 178 | ) 179 | } 180 | wit_parser::TypeDefKind::Enum(e) => match expansion { 181 | Expansion::Expanded(col) => { 182 | let fields = e 183 | .cases 184 | .iter() 185 | .map(|c| format!("{}{}", " ".repeat(col as usize * 4), c.name)) 186 | .collect::>() 187 | .join(",\n"); 188 | format!( 189 | "enum {{\n{fields}\n{}}}", 190 | " ".repeat((col - 1) as usize * 4) 191 | ) 192 | } 193 | Expansion::Collapsed => typ.name.clone().unwrap(), 194 | }, 195 | wit_parser::TypeDefKind::Record(r) => match expansion { 196 | Expansion::Expanded(col) => { 197 | let fields = r 198 | .fields 199 | .iter() 200 | .map(|f| { 201 | format!( 202 | "{}: {}", 203 | f.name, 204 | self.display_wit_type(&f.ty, Expansion::Collapsed) 205 | ) 206 | }) 207 | .map(|f| format!("{}{}", " ".repeat(col as usize * 4), f)) 208 | .collect::>() 209 | .join(",\n"); 210 | format!( 211 | "record {{\n{fields}\n{}}}", 212 | " ".repeat((col - 1) as usize * 4) 213 | ) 214 | } 215 | Expansion::Collapsed => typ.name.clone().unwrap(), 216 | }, 217 | wit_parser::TypeDefKind::Variant(v) => match expansion { 218 | Expansion::Expanded(col) => { 219 | let cases = v 220 | .cases 221 | .iter() 222 | .map(|c| { 223 | let data = match c.ty { 224 | Some(ty) => { 225 | let ty = self.display_wit_type(&ty, Expansion::Collapsed); 226 | format!("({ty})") 227 | } 228 | None => String::new(), 229 | }; 230 | format!("{}{}", c.name, data) 231 | }) 232 | .map(|f| format!("{}{}", " ".repeat(col as usize * 4), f)) 233 | .collect::>() 234 | .join(",\n"); 235 | format!( 236 | "variant {{\n{cases}\n{}}}", 237 | " ".repeat((col - 1) as usize * 4) 238 | ) 239 | } 240 | Expansion::Collapsed => typ.name.clone().unwrap(), 241 | }, 242 | // TODO: Fill these in with more information. 243 | wit_parser::TypeDefKind::Resource => "resource<...>".into(), 244 | wit_parser::TypeDefKind::Handle(_) => "handle<...>".into(), 245 | wit_parser::TypeDefKind::Flags(_) => "flags<...>".into(), 246 | wit_parser::TypeDefKind::Future(_) => "future<...>".into(), 247 | wit_parser::TypeDefKind::Stream(_) => "stream<...>".into(), 248 | wit_parser::TypeDefKind::Unknown => unreachable!(), 249 | }; 250 | Cow::Owned(display) 251 | } 252 | 253 | /// Whether the wasi cli (0.2.0) package is imported. 254 | /// 255 | /// Note that this is being used as a heuristic to determine whether to 256 | /// link wasi command. 257 | pub fn imports_wasi_cli(&self) -> bool { 258 | for package in self.package_dependencies() { 259 | if package.name.namespace == "wasi" 260 | && package.name.name == "cli" 261 | && package 262 | .name 263 | .version 264 | .as_ref() 265 | .map(|v| v.major == 0 && v.minor == 2 && v.patch == 0) 266 | .unwrap_or(false) 267 | { 268 | return true; 269 | } 270 | } 271 | false 272 | } 273 | 274 | /// All packages that are imported dependencies of the current world. 275 | pub fn package_dependencies(&self) -> impl Iterator { 276 | self.world() 277 | .imports 278 | .iter() 279 | .filter_map(move |(import_name, _)| { 280 | if let WorldKey::Interface(interface_id) = import_name { 281 | let interface = self.resolve.interfaces.get(*interface_id).unwrap(); 282 | interface 283 | .package 284 | .and_then(|package_id| self.resolve.packages.get(package_id)) 285 | } else { 286 | None 287 | } 288 | }) 289 | } 290 | 291 | pub fn world_item_name(&self, name: &WorldKey) -> String { 292 | self.resolve.name_world_key(name) 293 | } 294 | 295 | pub fn interface_name(&self, interface: &InterfaceId) -> Option { 296 | self.resolve.id_of(*interface) 297 | } 298 | 299 | pub fn world(&self) -> &World { 300 | self.resolve 301 | .worlds 302 | .get(self.world_id) 303 | .expect("world_id is not found in the resolved wit package") 304 | } 305 | 306 | pub(crate) fn imports( 307 | &self, 308 | include_wasi_cli: bool, 309 | ) -> impl Iterator { 310 | self.world() 311 | .imports 312 | .iter() 313 | .filter(move |(_, item)| match item { 314 | WorldItem::Interface { id, .. } if !include_wasi_cli => { 315 | let interface = self.interface_by_id(*id).unwrap(); 316 | let Some(package) = interface.package else { 317 | return true; 318 | }; 319 | let package = self.resolve.packages.get(package).unwrap(); 320 | !(package.name.namespace == "wasi" 321 | // This is not 100% correct. 322 | // We're assuming that all interfaces in these packages are handled by `wasmtime-wasi` 323 | // command implementation. This should be true for many components so we'll leave 324 | // the hack for now. 325 | && ["cli", "io", "filesystem", "random"].contains(&package.name.name.as_str())) 326 | } 327 | _ => true, 328 | }) 329 | } 330 | 331 | pub(crate) fn world_name(&self) -> String { 332 | let world = self.world(); 333 | let world_package = if let Some(world_package) = world.package { 334 | let world_package = self.resolve.packages.get(world_package).unwrap(); 335 | format!("{}/", world_package.name) 336 | } else { 337 | String::new() 338 | }; 339 | format!("{world_package}{}", world.name) 340 | } 341 | 342 | /// Get an interface by its ident from a list of world items. 343 | fn interface_in_items<'a>( 344 | &self, 345 | ident: parser::InterfaceIdent, 346 | items: impl Iterator, 347 | ) -> Option<&Interface> { 348 | let item = self.get_world_item_by_name(items, &ident.to_string())?; 349 | if let WorldItem::Interface { id, .. } = item { 350 | return self.interface_by_id(*id); 351 | } 352 | None 353 | } 354 | 355 | fn get_world_item_by_name<'a>( 356 | &self, 357 | mut items: impl Iterator, 358 | name: &str, 359 | ) -> Option<&'a WorldItem> { 360 | items.find_map(|(export_name, export)| { 361 | let export_name = self.resolve.name_world_key(export_name); 362 | (export_name == name).then_some(export) 363 | }) 364 | } 365 | } 366 | 367 | pub enum Expansion { 368 | Expanded(u8), 369 | Collapsed, 370 | } 371 | --------------------------------------------------------------------------------