├── .dockerignore ├── .github └── workflows │ ├── docker-publish.yml │ └── test.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── README.md ├── examples ├── .gitignore ├── binary-echo │ ├── .cargo │ │ └── config │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── clock │ ├── .cargo │ │ └── config │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── counter-service │ ├── .cargo │ │ └── config │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── cpu-hog │ ├── .cargo │ │ └── config │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── echo-server │ ├── .cargo │ │ └── config │ ├── Cargo.lock │ ├── Cargo.toml │ ├── dist │ │ └── server.wasm │ └── src │ │ └── lib.rs └── randomness │ ├── .cargo │ └── config │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ └── lib.rs ├── stateroom-cli ├── Cargo.toml ├── README.md └── src │ ├── bin │ └── stateroom.rs │ ├── build_util.rs │ ├── cli_opts.rs │ ├── commands │ ├── build.rs │ ├── dev.rs │ ├── mod.rs │ └── serve.rs │ ├── config.rs │ └── lib.rs ├── stateroom-server ├── Cargo.toml └── src │ ├── lib.rs │ └── server.rs ├── stateroom-wasm-host ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ ├── wasm_host.rs │ └── wasm_host_factory.rs ├── stateroom-wasm ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── stateroom-wasm-macro │ ├── Cargo.toml │ ├── README.md │ └── src │ └── lib.rs └── stateroom ├── Cargo.toml └── src ├── client_id.rs ├── lib.rs ├── message_recipient.rs └── messages.rs /.dockerignore: -------------------------------------------------------------------------------- 1 | **/target 2 | 3 | -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Build and push image 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | tags: [ 'v*.*.*' ] 7 | pull_request: 8 | branches: [ main ] 9 | 10 | env: 11 | REGISTRY: ghcr.io 12 | IMAGE_NAME: ${{ github.repository }} 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | permissions: 18 | contents: read 19 | packages: write 20 | id-token: write 21 | 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v2 25 | 26 | - uses: depot/setup-action@v1 27 | 28 | - name: Log into registry ${{ env.REGISTRY }} 29 | if: github.event_name != 'pull_request' 30 | uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c 31 | with: 32 | registry: ${{ env.REGISTRY }} 33 | username: ${{ github.actor }} 34 | password: ${{ secrets.GITHUB_TOKEN }} 35 | 36 | - name: Extract Docker metadata 37 | id: meta 38 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 39 | with: 40 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 41 | tags: | 42 | type=sha,enable=true,priority=100,prefix=sha-,suffix=,format=short 43 | type=raw,value=latest 44 | 45 | - name: Build and push Docker image 46 | id: build-and-push 47 | uses: depot/build-push-action@v1 48 | with: 49 | project: 58j0517pw2 50 | context: . 51 | push: ${{ github.event_name != 'pull_request' }} 52 | platforms: linux/amd64,linux/arm64 53 | tags: ${{ steps.meta.outputs.tags }} 54 | labels: ${{ steps.meta.outputs.labels }} 55 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Update rust 17 | uses: actions-rs/toolchain@v1 18 | with: 19 | toolchain: stable 20 | override: true 21 | - name: Run tests 22 | run: cargo test 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /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.22.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" 10 | dependencies = [ 11 | "gimli 0.29.0", 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.3" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 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.15" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" 61 | dependencies = [ 62 | "anstyle", 63 | "anstyle-parse", 64 | "anstyle-query", 65 | "anstyle-wincon", 66 | "colorchoice", 67 | "is_terminal_polyfill", 68 | "utf8parse", 69 | ] 70 | 71 | [[package]] 72 | name = "anstyle" 73 | version = "1.0.8" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 76 | 77 | [[package]] 78 | name = "anstyle-parse" 79 | version = "0.2.5" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" 82 | dependencies = [ 83 | "utf8parse", 84 | ] 85 | 86 | [[package]] 87 | name = "anstyle-query" 88 | version = "1.1.1" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" 91 | dependencies = [ 92 | "windows-sys 0.52.0", 93 | ] 94 | 95 | [[package]] 96 | name = "anstyle-wincon" 97 | version = "3.0.4" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" 100 | dependencies = [ 101 | "anstyle", 102 | "windows-sys 0.52.0", 103 | ] 104 | 105 | [[package]] 106 | name = "anyhow" 107 | version = "1.0.86" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" 110 | 111 | [[package]] 112 | name = "arbitrary" 113 | version = "1.3.2" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" 116 | 117 | [[package]] 118 | name = "async-trait" 119 | version = "0.1.82" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" 122 | dependencies = [ 123 | "proc-macro2", 124 | "quote", 125 | "syn 2.0.77", 126 | ] 127 | 128 | [[package]] 129 | name = "autocfg" 130 | version = "1.3.0" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 133 | 134 | [[package]] 135 | name = "axum" 136 | version = "0.7.5" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" 139 | dependencies = [ 140 | "async-trait", 141 | "axum-core", 142 | "base64 0.21.7", 143 | "bytes", 144 | "futures-util", 145 | "http", 146 | "http-body", 147 | "http-body-util", 148 | "hyper", 149 | "hyper-util", 150 | "itoa", 151 | "matchit", 152 | "memchr", 153 | "mime", 154 | "percent-encoding", 155 | "pin-project-lite", 156 | "rustversion", 157 | "serde", 158 | "serde_json", 159 | "serde_path_to_error", 160 | "serde_urlencoded", 161 | "sha1", 162 | "sync_wrapper 1.0.1", 163 | "tokio", 164 | "tokio-tungstenite", 165 | "tower", 166 | "tower-layer", 167 | "tower-service", 168 | "tracing", 169 | ] 170 | 171 | [[package]] 172 | name = "axum-core" 173 | version = "0.4.3" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" 176 | dependencies = [ 177 | "async-trait", 178 | "bytes", 179 | "futures-util", 180 | "http", 181 | "http-body", 182 | "http-body-util", 183 | "mime", 184 | "pin-project-lite", 185 | "rustversion", 186 | "sync_wrapper 0.1.2", 187 | "tower-layer", 188 | "tower-service", 189 | "tracing", 190 | ] 191 | 192 | [[package]] 193 | name = "backtrace" 194 | version = "0.3.73" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" 197 | dependencies = [ 198 | "addr2line", 199 | "cc", 200 | "cfg-if", 201 | "libc", 202 | "miniz_oxide", 203 | "object", 204 | "rustc-demangle", 205 | ] 206 | 207 | [[package]] 208 | name = "base64" 209 | version = "0.21.7" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 212 | 213 | [[package]] 214 | name = "base64" 215 | version = "0.22.1" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 218 | 219 | [[package]] 220 | name = "bincode" 221 | version = "1.3.3" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 224 | dependencies = [ 225 | "serde", 226 | ] 227 | 228 | [[package]] 229 | name = "bitflags" 230 | version = "2.6.0" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 233 | 234 | [[package]] 235 | name = "block-buffer" 236 | version = "0.10.4" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 239 | dependencies = [ 240 | "generic-array", 241 | ] 242 | 243 | [[package]] 244 | name = "bumpalo" 245 | version = "3.16.0" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 248 | 249 | [[package]] 250 | name = "byteorder" 251 | version = "1.5.0" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 254 | 255 | [[package]] 256 | name = "bytes" 257 | version = "1.7.1" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" 260 | 261 | [[package]] 262 | name = "camino" 263 | version = "1.1.9" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" 266 | dependencies = [ 267 | "serde", 268 | ] 269 | 270 | [[package]] 271 | name = "cap-fs-ext" 272 | version = "3.2.0" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "eb23061fc1c4ead4e45ca713080fe768e6234e959f5a5c399c39eb41aa34e56e" 275 | dependencies = [ 276 | "cap-primitives", 277 | "cap-std", 278 | "io-lifetimes", 279 | "windows-sys 0.52.0", 280 | ] 281 | 282 | [[package]] 283 | name = "cap-primitives" 284 | version = "3.2.0" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "6d00bd8d26c4270d950eaaa837387964a2089a1c3c349a690a1fa03221d29531" 287 | dependencies = [ 288 | "ambient-authority", 289 | "fs-set-times", 290 | "io-extras", 291 | "io-lifetimes", 292 | "ipnet", 293 | "maybe-owned", 294 | "rustix", 295 | "windows-sys 0.52.0", 296 | "winx", 297 | ] 298 | 299 | [[package]] 300 | name = "cap-rand" 301 | version = "3.2.0" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "dbcb16a619d8b8211ed61f42bd290d2a1ac71277a69cf8417ec0996fa92f5211" 304 | dependencies = [ 305 | "ambient-authority", 306 | "rand", 307 | ] 308 | 309 | [[package]] 310 | name = "cap-std" 311 | version = "3.2.0" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "19eb8e3d71996828751c1ed3908a439639752ac6bdc874e41469ef7fc15fbd7f" 314 | dependencies = [ 315 | "cap-primitives", 316 | "io-extras", 317 | "io-lifetimes", 318 | "rustix", 319 | ] 320 | 321 | [[package]] 322 | name = "cap-time-ext" 323 | version = "3.2.0" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "61142dc51e25b7acc970ca578ce2c3695eac22bbba46c1073f5f583e78957725" 326 | dependencies = [ 327 | "ambient-authority", 328 | "cap-primitives", 329 | "iana-time-zone", 330 | "once_cell", 331 | "rustix", 332 | "winx", 333 | ] 334 | 335 | [[package]] 336 | name = "cargo-platform" 337 | version = "0.1.8" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" 340 | dependencies = [ 341 | "serde", 342 | ] 343 | 344 | [[package]] 345 | name = "cargo_metadata" 346 | version = "0.18.1" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" 349 | dependencies = [ 350 | "camino", 351 | "cargo-platform", 352 | "semver", 353 | "serde", 354 | "serde_json", 355 | "thiserror", 356 | ] 357 | 358 | [[package]] 359 | name = "cc" 360 | version = "1.1.15" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" 363 | dependencies = [ 364 | "jobserver", 365 | "libc", 366 | "shlex", 367 | ] 368 | 369 | [[package]] 370 | name = "cfg-if" 371 | version = "1.0.0" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 374 | 375 | [[package]] 376 | name = "clap" 377 | version = "4.5.16" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" 380 | dependencies = [ 381 | "clap_builder", 382 | "clap_derive", 383 | ] 384 | 385 | [[package]] 386 | name = "clap_builder" 387 | version = "4.5.15" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" 390 | dependencies = [ 391 | "anstream", 392 | "anstyle", 393 | "clap_lex", 394 | "strsim", 395 | ] 396 | 397 | [[package]] 398 | name = "clap_derive" 399 | version = "4.5.13" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" 402 | dependencies = [ 403 | "heck 0.5.0", 404 | "proc-macro2", 405 | "quote", 406 | "syn 2.0.77", 407 | ] 408 | 409 | [[package]] 410 | name = "clap_lex" 411 | version = "0.7.2" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" 414 | 415 | [[package]] 416 | name = "cobs" 417 | version = "0.2.3" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" 420 | 421 | [[package]] 422 | name = "colorchoice" 423 | version = "1.0.2" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" 426 | 427 | [[package]] 428 | name = "core-foundation-sys" 429 | version = "0.8.7" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 432 | 433 | [[package]] 434 | name = "cpp_demangle" 435 | version = "0.4.4" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d" 438 | dependencies = [ 439 | "cfg-if", 440 | ] 441 | 442 | [[package]] 443 | name = "cpufeatures" 444 | version = "0.2.13" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" 447 | dependencies = [ 448 | "libc", 449 | ] 450 | 451 | [[package]] 452 | name = "cranelift-bforest" 453 | version = "0.111.0" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "b80c3a50b9c4c7e5b5f73c0ed746687774fc9e36ef652b110da8daebf0c6e0e6" 456 | dependencies = [ 457 | "cranelift-entity", 458 | ] 459 | 460 | [[package]] 461 | name = "cranelift-bitset" 462 | version = "0.111.0" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "38778758c2ca918b05acb2199134e0c561fb577c50574259b26190b6c2d95ded" 465 | dependencies = [ 466 | "serde", 467 | "serde_derive", 468 | ] 469 | 470 | [[package]] 471 | name = "cranelift-codegen" 472 | version = "0.111.0" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "58258667ad10e468bfc13a8d620f50dfcd4bb35d668123e97defa2549b9ad397" 475 | dependencies = [ 476 | "bumpalo", 477 | "cranelift-bforest", 478 | "cranelift-bitset", 479 | "cranelift-codegen-meta", 480 | "cranelift-codegen-shared", 481 | "cranelift-control", 482 | "cranelift-entity", 483 | "cranelift-isle", 484 | "gimli 0.29.0", 485 | "hashbrown 0.14.5", 486 | "log", 487 | "regalloc2", 488 | "rustc-hash", 489 | "smallvec", 490 | "target-lexicon", 491 | ] 492 | 493 | [[package]] 494 | name = "cranelift-codegen-meta" 495 | version = "0.111.0" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "043f0b702e529dcb07ff92bd7d40e7d5317b5493595172c5eb0983343751ee06" 498 | dependencies = [ 499 | "cranelift-codegen-shared", 500 | ] 501 | 502 | [[package]] 503 | name = "cranelift-codegen-shared" 504 | version = "0.111.0" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "7763578888ab53eca5ce7da141953f828e82c2bfadcffc106d10d1866094ffbb" 507 | 508 | [[package]] 509 | name = "cranelift-control" 510 | version = "0.111.0" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "32db15f08c05df570f11e8ab33cb1ec449a64b37c8a3498377b77650bef33d8b" 513 | dependencies = [ 514 | "arbitrary", 515 | ] 516 | 517 | [[package]] 518 | name = "cranelift-entity" 519 | version = "0.111.0" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "5289cdb399381a27e7bbfa1b42185916007c3d49aeef70b1d01cb4caa8010130" 522 | dependencies = [ 523 | "cranelift-bitset", 524 | "serde", 525 | "serde_derive", 526 | ] 527 | 528 | [[package]] 529 | name = "cranelift-frontend" 530 | version = "0.111.0" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "31ba8ab24eb9470477e98ddfa3c799a649ac5a0d9a2042868c4c952133c234e8" 533 | dependencies = [ 534 | "cranelift-codegen", 535 | "log", 536 | "smallvec", 537 | "target-lexicon", 538 | ] 539 | 540 | [[package]] 541 | name = "cranelift-isle" 542 | version = "0.111.0" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "2b72a3c5c166a70426dcb209bdd0bb71a787c1ea76023dc0974fbabca770e8f9" 545 | 546 | [[package]] 547 | name = "cranelift-native" 548 | version = "0.111.0" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "46a42424c956bbc31fc5c2706073df896156c5420ae8fa2a5d48dbc7b295d71b" 551 | dependencies = [ 552 | "cranelift-codegen", 553 | "libc", 554 | "target-lexicon", 555 | ] 556 | 557 | [[package]] 558 | name = "cranelift-wasm" 559 | version = "0.111.0" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "49778df4289933d735b93c30a345513e030cf83101de0036e19b760f8aa09f68" 562 | dependencies = [ 563 | "cranelift-codegen", 564 | "cranelift-entity", 565 | "cranelift-frontend", 566 | "itertools", 567 | "log", 568 | "smallvec", 569 | "wasmparser 0.215.0", 570 | "wasmtime-types", 571 | ] 572 | 573 | [[package]] 574 | name = "crc32fast" 575 | version = "1.4.2" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 578 | dependencies = [ 579 | "cfg-if", 580 | ] 581 | 582 | [[package]] 583 | name = "crossbeam-deque" 584 | version = "0.8.5" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 587 | dependencies = [ 588 | "crossbeam-epoch", 589 | "crossbeam-utils", 590 | ] 591 | 592 | [[package]] 593 | name = "crossbeam-epoch" 594 | version = "0.9.18" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 597 | dependencies = [ 598 | "crossbeam-utils", 599 | ] 600 | 601 | [[package]] 602 | name = "crossbeam-utils" 603 | version = "0.8.20" 604 | source = "registry+https://github.com/rust-lang/crates.io-index" 605 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 606 | 607 | [[package]] 608 | name = "crypto-common" 609 | version = "0.1.6" 610 | source = "registry+https://github.com/rust-lang/crates.io-index" 611 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 612 | dependencies = [ 613 | "generic-array", 614 | "typenum", 615 | ] 616 | 617 | [[package]] 618 | name = "dashmap" 619 | version = "6.0.1" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28" 622 | dependencies = [ 623 | "cfg-if", 624 | "crossbeam-utils", 625 | "hashbrown 0.14.5", 626 | "lock_api", 627 | "once_cell", 628 | "parking_lot_core", 629 | ] 630 | 631 | [[package]] 632 | name = "data-encoding" 633 | version = "2.6.0" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" 636 | 637 | [[package]] 638 | name = "debugid" 639 | version = "0.8.0" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" 642 | dependencies = [ 643 | "uuid", 644 | ] 645 | 646 | [[package]] 647 | name = "digest" 648 | version = "0.10.7" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 651 | dependencies = [ 652 | "block-buffer", 653 | "crypto-common", 654 | ] 655 | 656 | [[package]] 657 | name = "directories-next" 658 | version = "2.0.0" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" 661 | dependencies = [ 662 | "cfg-if", 663 | "dirs-sys-next", 664 | ] 665 | 666 | [[package]] 667 | name = "dirs" 668 | version = "4.0.0" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" 671 | dependencies = [ 672 | "dirs-sys", 673 | ] 674 | 675 | [[package]] 676 | name = "dirs-sys" 677 | version = "0.3.7" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" 680 | dependencies = [ 681 | "libc", 682 | "redox_users", 683 | "winapi", 684 | ] 685 | 686 | [[package]] 687 | name = "dirs-sys-next" 688 | version = "0.1.2" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 691 | dependencies = [ 692 | "libc", 693 | "redox_users", 694 | "winapi", 695 | ] 696 | 697 | [[package]] 698 | name = "either" 699 | version = "1.13.0" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 702 | 703 | [[package]] 704 | name = "embedded-io" 705 | version = "0.4.0" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" 708 | 709 | [[package]] 710 | name = "embedded-io" 711 | version = "0.6.1" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 714 | 715 | [[package]] 716 | name = "encoding_rs" 717 | version = "0.8.34" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" 720 | dependencies = [ 721 | "cfg-if", 722 | ] 723 | 724 | [[package]] 725 | name = "equivalent" 726 | version = "1.0.1" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 729 | 730 | [[package]] 731 | name = "errno" 732 | version = "0.3.9" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 735 | dependencies = [ 736 | "libc", 737 | "windows-sys 0.52.0", 738 | ] 739 | 740 | [[package]] 741 | name = "fallible-iterator" 742 | version = "0.2.0" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 745 | 746 | [[package]] 747 | name = "fallible-iterator" 748 | version = "0.3.0" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 751 | 752 | [[package]] 753 | name = "fastrand" 754 | version = "2.1.1" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" 757 | 758 | [[package]] 759 | name = "fd-lock" 760 | version = "4.0.2" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" 763 | dependencies = [ 764 | "cfg-if", 765 | "rustix", 766 | "windows-sys 0.52.0", 767 | ] 768 | 769 | [[package]] 770 | name = "fnv" 771 | version = "1.0.7" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 774 | 775 | [[package]] 776 | name = "form_urlencoded" 777 | version = "1.2.1" 778 | source = "registry+https://github.com/rust-lang/crates.io-index" 779 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 780 | dependencies = [ 781 | "percent-encoding", 782 | ] 783 | 784 | [[package]] 785 | name = "fs-set-times" 786 | version = "0.20.1" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb" 789 | dependencies = [ 790 | "io-lifetimes", 791 | "rustix", 792 | "windows-sys 0.52.0", 793 | ] 794 | 795 | [[package]] 796 | name = "fs_extra" 797 | version = "1.3.0" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" 800 | 801 | [[package]] 802 | name = "futures-channel" 803 | version = "0.3.30" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 806 | dependencies = [ 807 | "futures-core", 808 | ] 809 | 810 | [[package]] 811 | name = "futures-core" 812 | version = "0.3.30" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 815 | 816 | [[package]] 817 | name = "futures-macro" 818 | version = "0.3.30" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 821 | dependencies = [ 822 | "proc-macro2", 823 | "quote", 824 | "syn 2.0.77", 825 | ] 826 | 827 | [[package]] 828 | name = "futures-sink" 829 | version = "0.3.30" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 832 | 833 | [[package]] 834 | name = "futures-task" 835 | version = "0.3.30" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 838 | 839 | [[package]] 840 | name = "futures-util" 841 | version = "0.3.30" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 844 | dependencies = [ 845 | "futures-core", 846 | "futures-macro", 847 | "futures-sink", 848 | "futures-task", 849 | "pin-project-lite", 850 | "pin-utils", 851 | "slab", 852 | ] 853 | 854 | [[package]] 855 | name = "fxhash" 856 | version = "0.2.1" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 859 | dependencies = [ 860 | "byteorder", 861 | ] 862 | 863 | [[package]] 864 | name = "fxprof-processed-profile" 865 | version = "0.6.0" 866 | source = "registry+https://github.com/rust-lang/crates.io-index" 867 | checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" 868 | dependencies = [ 869 | "bitflags", 870 | "debugid", 871 | "fxhash", 872 | "serde", 873 | "serde_json", 874 | ] 875 | 876 | [[package]] 877 | name = "generic-array" 878 | version = "0.14.7" 879 | source = "registry+https://github.com/rust-lang/crates.io-index" 880 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 881 | dependencies = [ 882 | "typenum", 883 | "version_check", 884 | ] 885 | 886 | [[package]] 887 | name = "getrandom" 888 | version = "0.2.15" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 891 | dependencies = [ 892 | "cfg-if", 893 | "libc", 894 | "wasi", 895 | ] 896 | 897 | [[package]] 898 | name = "gimli" 899 | version = "0.26.2" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" 902 | dependencies = [ 903 | "fallible-iterator 0.2.0", 904 | "indexmap 1.9.3", 905 | "stable_deref_trait", 906 | ] 907 | 908 | [[package]] 909 | name = "gimli" 910 | version = "0.29.0" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" 913 | dependencies = [ 914 | "fallible-iterator 0.3.0", 915 | "indexmap 2.5.0", 916 | "stable_deref_trait", 917 | ] 918 | 919 | [[package]] 920 | name = "hashbrown" 921 | version = "0.12.3" 922 | source = "registry+https://github.com/rust-lang/crates.io-index" 923 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 924 | 925 | [[package]] 926 | name = "hashbrown" 927 | version = "0.13.2" 928 | source = "registry+https://github.com/rust-lang/crates.io-index" 929 | checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" 930 | dependencies = [ 931 | "ahash", 932 | ] 933 | 934 | [[package]] 935 | name = "hashbrown" 936 | version = "0.14.5" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 939 | dependencies = [ 940 | "ahash", 941 | "serde", 942 | ] 943 | 944 | [[package]] 945 | name = "heck" 946 | version = "0.3.3" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 949 | dependencies = [ 950 | "unicode-segmentation", 951 | ] 952 | 953 | [[package]] 954 | name = "heck" 955 | version = "0.4.1" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 958 | 959 | [[package]] 960 | name = "heck" 961 | version = "0.5.0" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 964 | 965 | [[package]] 966 | name = "hermit-abi" 967 | version = "0.3.9" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 970 | 971 | [[package]] 972 | name = "http" 973 | version = "1.1.0" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" 976 | dependencies = [ 977 | "bytes", 978 | "fnv", 979 | "itoa", 980 | ] 981 | 982 | [[package]] 983 | name = "http-body" 984 | version = "1.0.1" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 987 | dependencies = [ 988 | "bytes", 989 | "http", 990 | ] 991 | 992 | [[package]] 993 | name = "http-body-util" 994 | version = "0.1.2" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" 997 | dependencies = [ 998 | "bytes", 999 | "futures-util", 1000 | "http", 1001 | "http-body", 1002 | "pin-project-lite", 1003 | ] 1004 | 1005 | [[package]] 1006 | name = "http-range-header" 1007 | version = "0.4.1" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" 1010 | 1011 | [[package]] 1012 | name = "httparse" 1013 | version = "1.9.4" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" 1016 | 1017 | [[package]] 1018 | name = "httpdate" 1019 | version = "1.0.3" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 1022 | 1023 | [[package]] 1024 | name = "hyper" 1025 | version = "1.4.1" 1026 | source = "registry+https://github.com/rust-lang/crates.io-index" 1027 | checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" 1028 | dependencies = [ 1029 | "bytes", 1030 | "futures-channel", 1031 | "futures-util", 1032 | "http", 1033 | "http-body", 1034 | "httparse", 1035 | "httpdate", 1036 | "itoa", 1037 | "pin-project-lite", 1038 | "smallvec", 1039 | "tokio", 1040 | ] 1041 | 1042 | [[package]] 1043 | name = "hyper-util" 1044 | version = "0.1.7" 1045 | source = "registry+https://github.com/rust-lang/crates.io-index" 1046 | checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" 1047 | dependencies = [ 1048 | "bytes", 1049 | "futures-util", 1050 | "http", 1051 | "http-body", 1052 | "hyper", 1053 | "pin-project-lite", 1054 | "tokio", 1055 | ] 1056 | 1057 | [[package]] 1058 | name = "iana-time-zone" 1059 | version = "0.1.60" 1060 | source = "registry+https://github.com/rust-lang/crates.io-index" 1061 | checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" 1062 | dependencies = [ 1063 | "android_system_properties", 1064 | "core-foundation-sys", 1065 | "iana-time-zone-haiku", 1066 | "js-sys", 1067 | "wasm-bindgen", 1068 | "windows-core", 1069 | ] 1070 | 1071 | [[package]] 1072 | name = "iana-time-zone-haiku" 1073 | version = "0.1.2" 1074 | source = "registry+https://github.com/rust-lang/crates.io-index" 1075 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 1076 | dependencies = [ 1077 | "cc", 1078 | ] 1079 | 1080 | [[package]] 1081 | name = "id-arena" 1082 | version = "2.2.1" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" 1085 | 1086 | [[package]] 1087 | name = "idna" 1088 | version = "0.5.0" 1089 | source = "registry+https://github.com/rust-lang/crates.io-index" 1090 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 1091 | dependencies = [ 1092 | "unicode-bidi", 1093 | "unicode-normalization", 1094 | ] 1095 | 1096 | [[package]] 1097 | name = "indexmap" 1098 | version = "1.9.3" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 1101 | dependencies = [ 1102 | "autocfg", 1103 | "hashbrown 0.12.3", 1104 | ] 1105 | 1106 | [[package]] 1107 | name = "indexmap" 1108 | version = "2.5.0" 1109 | source = "registry+https://github.com/rust-lang/crates.io-index" 1110 | checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" 1111 | dependencies = [ 1112 | "equivalent", 1113 | "hashbrown 0.14.5", 1114 | "serde", 1115 | ] 1116 | 1117 | [[package]] 1118 | name = "io-extras" 1119 | version = "0.18.2" 1120 | source = "registry+https://github.com/rust-lang/crates.io-index" 1121 | checksum = "c9f046b9af244f13b3bd939f55d16830ac3a201e8a9ba9661bfcb03e2be72b9b" 1122 | dependencies = [ 1123 | "io-lifetimes", 1124 | "windows-sys 0.52.0", 1125 | ] 1126 | 1127 | [[package]] 1128 | name = "io-lifetimes" 1129 | version = "2.0.3" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" 1132 | 1133 | [[package]] 1134 | name = "ipnet" 1135 | version = "2.9.0" 1136 | source = "registry+https://github.com/rust-lang/crates.io-index" 1137 | checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" 1138 | 1139 | [[package]] 1140 | name = "is_terminal_polyfill" 1141 | version = "1.70.1" 1142 | source = "registry+https://github.com/rust-lang/crates.io-index" 1143 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 1144 | 1145 | [[package]] 1146 | name = "itertools" 1147 | version = "0.12.1" 1148 | source = "registry+https://github.com/rust-lang/crates.io-index" 1149 | checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 1150 | dependencies = [ 1151 | "either", 1152 | ] 1153 | 1154 | [[package]] 1155 | name = "itoa" 1156 | version = "1.0.11" 1157 | source = "registry+https://github.com/rust-lang/crates.io-index" 1158 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 1159 | 1160 | [[package]] 1161 | name = "ittapi" 1162 | version = "0.4.0" 1163 | source = "registry+https://github.com/rust-lang/crates.io-index" 1164 | checksum = "6b996fe614c41395cdaedf3cf408a9534851090959d90d54a535f675550b64b1" 1165 | dependencies = [ 1166 | "anyhow", 1167 | "ittapi-sys", 1168 | "log", 1169 | ] 1170 | 1171 | [[package]] 1172 | name = "ittapi-sys" 1173 | version = "0.4.0" 1174 | source = "registry+https://github.com/rust-lang/crates.io-index" 1175 | checksum = "52f5385394064fa2c886205dba02598013ce83d3e92d33dbdc0c52fe0e7bf4fc" 1176 | dependencies = [ 1177 | "cc", 1178 | ] 1179 | 1180 | [[package]] 1181 | name = "jobserver" 1182 | version = "0.1.32" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" 1185 | dependencies = [ 1186 | "libc", 1187 | ] 1188 | 1189 | [[package]] 1190 | name = "js-sys" 1191 | version = "0.3.70" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" 1194 | dependencies = [ 1195 | "wasm-bindgen", 1196 | ] 1197 | 1198 | [[package]] 1199 | name = "lazy_static" 1200 | version = "1.5.0" 1201 | source = "registry+https://github.com/rust-lang/crates.io-index" 1202 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1203 | 1204 | [[package]] 1205 | name = "leb128" 1206 | version = "0.2.5" 1207 | source = "registry+https://github.com/rust-lang/crates.io-index" 1208 | checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" 1209 | 1210 | [[package]] 1211 | name = "libc" 1212 | version = "0.2.158" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" 1215 | 1216 | [[package]] 1217 | name = "libm" 1218 | version = "0.2.8" 1219 | source = "registry+https://github.com/rust-lang/crates.io-index" 1220 | checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" 1221 | 1222 | [[package]] 1223 | name = "libredox" 1224 | version = "0.1.3" 1225 | source = "registry+https://github.com/rust-lang/crates.io-index" 1226 | checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" 1227 | dependencies = [ 1228 | "bitflags", 1229 | "libc", 1230 | ] 1231 | 1232 | [[package]] 1233 | name = "linux-raw-sys" 1234 | version = "0.4.14" 1235 | source = "registry+https://github.com/rust-lang/crates.io-index" 1236 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 1237 | 1238 | [[package]] 1239 | name = "lock_api" 1240 | version = "0.4.12" 1241 | source = "registry+https://github.com/rust-lang/crates.io-index" 1242 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 1243 | dependencies = [ 1244 | "autocfg", 1245 | "scopeguard", 1246 | ] 1247 | 1248 | [[package]] 1249 | name = "log" 1250 | version = "0.4.22" 1251 | source = "registry+https://github.com/rust-lang/crates.io-index" 1252 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 1253 | 1254 | [[package]] 1255 | name = "mach2" 1256 | version = "0.4.2" 1257 | source = "registry+https://github.com/rust-lang/crates.io-index" 1258 | checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" 1259 | dependencies = [ 1260 | "libc", 1261 | ] 1262 | 1263 | [[package]] 1264 | name = "matchers" 1265 | version = "0.1.0" 1266 | source = "registry+https://github.com/rust-lang/crates.io-index" 1267 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 1268 | dependencies = [ 1269 | "regex-automata 0.1.10", 1270 | ] 1271 | 1272 | [[package]] 1273 | name = "matchit" 1274 | version = "0.7.3" 1275 | source = "registry+https://github.com/rust-lang/crates.io-index" 1276 | checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" 1277 | 1278 | [[package]] 1279 | name = "maybe-owned" 1280 | version = "0.3.4" 1281 | source = "registry+https://github.com/rust-lang/crates.io-index" 1282 | checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" 1283 | 1284 | [[package]] 1285 | name = "memchr" 1286 | version = "2.7.4" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 1289 | 1290 | [[package]] 1291 | name = "memfd" 1292 | version = "0.6.4" 1293 | source = "registry+https://github.com/rust-lang/crates.io-index" 1294 | checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" 1295 | dependencies = [ 1296 | "rustix", 1297 | ] 1298 | 1299 | [[package]] 1300 | name = "mime" 1301 | version = "0.3.17" 1302 | source = "registry+https://github.com/rust-lang/crates.io-index" 1303 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1304 | 1305 | [[package]] 1306 | name = "mime_guess" 1307 | version = "2.0.5" 1308 | source = "registry+https://github.com/rust-lang/crates.io-index" 1309 | checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" 1310 | dependencies = [ 1311 | "mime", 1312 | "unicase", 1313 | ] 1314 | 1315 | [[package]] 1316 | name = "miniz_oxide" 1317 | version = "0.7.4" 1318 | source = "registry+https://github.com/rust-lang/crates.io-index" 1319 | checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" 1320 | dependencies = [ 1321 | "adler", 1322 | ] 1323 | 1324 | [[package]] 1325 | name = "mio" 1326 | version = "1.0.2" 1327 | source = "registry+https://github.com/rust-lang/crates.io-index" 1328 | checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" 1329 | dependencies = [ 1330 | "hermit-abi", 1331 | "libc", 1332 | "wasi", 1333 | "windows-sys 0.52.0", 1334 | ] 1335 | 1336 | [[package]] 1337 | name = "nu-ansi-term" 1338 | version = "0.46.0" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 1341 | dependencies = [ 1342 | "overload", 1343 | "winapi", 1344 | ] 1345 | 1346 | [[package]] 1347 | name = "object" 1348 | version = "0.36.4" 1349 | source = "registry+https://github.com/rust-lang/crates.io-index" 1350 | checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" 1351 | dependencies = [ 1352 | "crc32fast", 1353 | "hashbrown 0.14.5", 1354 | "indexmap 2.5.0", 1355 | "memchr", 1356 | ] 1357 | 1358 | [[package]] 1359 | name = "once_cell" 1360 | version = "1.19.0" 1361 | source = "registry+https://github.com/rust-lang/crates.io-index" 1362 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 1363 | 1364 | [[package]] 1365 | name = "overload" 1366 | version = "0.1.1" 1367 | source = "registry+https://github.com/rust-lang/crates.io-index" 1368 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 1369 | 1370 | [[package]] 1371 | name = "parking_lot_core" 1372 | version = "0.9.10" 1373 | source = "registry+https://github.com/rust-lang/crates.io-index" 1374 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1375 | dependencies = [ 1376 | "cfg-if", 1377 | "libc", 1378 | "redox_syscall", 1379 | "smallvec", 1380 | "windows-targets", 1381 | ] 1382 | 1383 | [[package]] 1384 | name = "paste" 1385 | version = "1.0.15" 1386 | source = "registry+https://github.com/rust-lang/crates.io-index" 1387 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1388 | 1389 | [[package]] 1390 | name = "percent-encoding" 1391 | version = "2.3.1" 1392 | source = "registry+https://github.com/rust-lang/crates.io-index" 1393 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1394 | 1395 | [[package]] 1396 | name = "pin-project" 1397 | version = "1.1.5" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" 1400 | dependencies = [ 1401 | "pin-project-internal", 1402 | ] 1403 | 1404 | [[package]] 1405 | name = "pin-project-internal" 1406 | version = "1.1.5" 1407 | source = "registry+https://github.com/rust-lang/crates.io-index" 1408 | checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" 1409 | dependencies = [ 1410 | "proc-macro2", 1411 | "quote", 1412 | "syn 2.0.77", 1413 | ] 1414 | 1415 | [[package]] 1416 | name = "pin-project-lite" 1417 | version = "0.2.14" 1418 | source = "registry+https://github.com/rust-lang/crates.io-index" 1419 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 1420 | 1421 | [[package]] 1422 | name = "pin-utils" 1423 | version = "0.1.0" 1424 | source = "registry+https://github.com/rust-lang/crates.io-index" 1425 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1426 | 1427 | [[package]] 1428 | name = "pkg-config" 1429 | version = "0.3.30" 1430 | source = "registry+https://github.com/rust-lang/crates.io-index" 1431 | checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 1432 | 1433 | [[package]] 1434 | name = "postcard" 1435 | version = "1.0.10" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" 1438 | dependencies = [ 1439 | "cobs", 1440 | "embedded-io 0.4.0", 1441 | "embedded-io 0.6.1", 1442 | "serde", 1443 | ] 1444 | 1445 | [[package]] 1446 | name = "ppv-lite86" 1447 | version = "0.2.20" 1448 | source = "registry+https://github.com/rust-lang/crates.io-index" 1449 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 1450 | dependencies = [ 1451 | "zerocopy", 1452 | ] 1453 | 1454 | [[package]] 1455 | name = "proc-macro2" 1456 | version = "1.0.86" 1457 | source = "registry+https://github.com/rust-lang/crates.io-index" 1458 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 1459 | dependencies = [ 1460 | "unicode-ident", 1461 | ] 1462 | 1463 | [[package]] 1464 | name = "psm" 1465 | version = "0.1.22" 1466 | source = "registry+https://github.com/rust-lang/crates.io-index" 1467 | checksum = "3b1f9bf148c15500d44581654fb9260bc9d82970f3ef777a79a40534f6aa784f" 1468 | dependencies = [ 1469 | "cc", 1470 | ] 1471 | 1472 | [[package]] 1473 | name = "quote" 1474 | version = "1.0.37" 1475 | source = "registry+https://github.com/rust-lang/crates.io-index" 1476 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 1477 | dependencies = [ 1478 | "proc-macro2", 1479 | ] 1480 | 1481 | [[package]] 1482 | name = "rand" 1483 | version = "0.8.5" 1484 | source = "registry+https://github.com/rust-lang/crates.io-index" 1485 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1486 | dependencies = [ 1487 | "libc", 1488 | "rand_chacha", 1489 | "rand_core", 1490 | ] 1491 | 1492 | [[package]] 1493 | name = "rand_chacha" 1494 | version = "0.3.1" 1495 | source = "registry+https://github.com/rust-lang/crates.io-index" 1496 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1497 | dependencies = [ 1498 | "ppv-lite86", 1499 | "rand_core", 1500 | ] 1501 | 1502 | [[package]] 1503 | name = "rand_core" 1504 | version = "0.6.4" 1505 | source = "registry+https://github.com/rust-lang/crates.io-index" 1506 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1507 | dependencies = [ 1508 | "getrandom", 1509 | ] 1510 | 1511 | [[package]] 1512 | name = "rayon" 1513 | version = "1.10.0" 1514 | source = "registry+https://github.com/rust-lang/crates.io-index" 1515 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 1516 | dependencies = [ 1517 | "either", 1518 | "rayon-core", 1519 | ] 1520 | 1521 | [[package]] 1522 | name = "rayon-core" 1523 | version = "1.12.1" 1524 | source = "registry+https://github.com/rust-lang/crates.io-index" 1525 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 1526 | dependencies = [ 1527 | "crossbeam-deque", 1528 | "crossbeam-utils", 1529 | ] 1530 | 1531 | [[package]] 1532 | name = "redox_syscall" 1533 | version = "0.5.3" 1534 | source = "registry+https://github.com/rust-lang/crates.io-index" 1535 | checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" 1536 | dependencies = [ 1537 | "bitflags", 1538 | ] 1539 | 1540 | [[package]] 1541 | name = "redox_users" 1542 | version = "0.4.6" 1543 | source = "registry+https://github.com/rust-lang/crates.io-index" 1544 | checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" 1545 | dependencies = [ 1546 | "getrandom", 1547 | "libredox", 1548 | "thiserror", 1549 | ] 1550 | 1551 | [[package]] 1552 | name = "regalloc2" 1553 | version = "0.9.3" 1554 | source = "registry+https://github.com/rust-lang/crates.io-index" 1555 | checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" 1556 | dependencies = [ 1557 | "hashbrown 0.13.2", 1558 | "log", 1559 | "rustc-hash", 1560 | "slice-group-by", 1561 | "smallvec", 1562 | ] 1563 | 1564 | [[package]] 1565 | name = "regex" 1566 | version = "1.10.6" 1567 | source = "registry+https://github.com/rust-lang/crates.io-index" 1568 | checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" 1569 | dependencies = [ 1570 | "aho-corasick", 1571 | "memchr", 1572 | "regex-automata 0.4.7", 1573 | "regex-syntax 0.8.4", 1574 | ] 1575 | 1576 | [[package]] 1577 | name = "regex-automata" 1578 | version = "0.1.10" 1579 | source = "registry+https://github.com/rust-lang/crates.io-index" 1580 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 1581 | dependencies = [ 1582 | "regex-syntax 0.6.29", 1583 | ] 1584 | 1585 | [[package]] 1586 | name = "regex-automata" 1587 | version = "0.4.7" 1588 | source = "registry+https://github.com/rust-lang/crates.io-index" 1589 | checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" 1590 | dependencies = [ 1591 | "aho-corasick", 1592 | "memchr", 1593 | "regex-syntax 0.8.4", 1594 | ] 1595 | 1596 | [[package]] 1597 | name = "regex-syntax" 1598 | version = "0.6.29" 1599 | source = "registry+https://github.com/rust-lang/crates.io-index" 1600 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 1601 | 1602 | [[package]] 1603 | name = "regex-syntax" 1604 | version = "0.8.4" 1605 | source = "registry+https://github.com/rust-lang/crates.io-index" 1606 | checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" 1607 | 1608 | [[package]] 1609 | name = "rustc-demangle" 1610 | version = "0.1.24" 1611 | source = "registry+https://github.com/rust-lang/crates.io-index" 1612 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1613 | 1614 | [[package]] 1615 | name = "rustc-hash" 1616 | version = "1.1.0" 1617 | source = "registry+https://github.com/rust-lang/crates.io-index" 1618 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1619 | 1620 | [[package]] 1621 | name = "rustix" 1622 | version = "0.38.35" 1623 | source = "registry+https://github.com/rust-lang/crates.io-index" 1624 | checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" 1625 | dependencies = [ 1626 | "bitflags", 1627 | "errno", 1628 | "itoa", 1629 | "libc", 1630 | "linux-raw-sys", 1631 | "once_cell", 1632 | "windows-sys 0.52.0", 1633 | ] 1634 | 1635 | [[package]] 1636 | name = "rustversion" 1637 | version = "1.0.17" 1638 | source = "registry+https://github.com/rust-lang/crates.io-index" 1639 | checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" 1640 | 1641 | [[package]] 1642 | name = "ryu" 1643 | version = "1.0.18" 1644 | source = "registry+https://github.com/rust-lang/crates.io-index" 1645 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 1646 | 1647 | [[package]] 1648 | name = "scopeguard" 1649 | version = "1.2.0" 1650 | source = "registry+https://github.com/rust-lang/crates.io-index" 1651 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1652 | 1653 | [[package]] 1654 | name = "semver" 1655 | version = "1.0.23" 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" 1657 | checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" 1658 | dependencies = [ 1659 | "serde", 1660 | ] 1661 | 1662 | [[package]] 1663 | name = "serde" 1664 | version = "1.0.209" 1665 | source = "registry+https://github.com/rust-lang/crates.io-index" 1666 | checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" 1667 | dependencies = [ 1668 | "serde_derive", 1669 | ] 1670 | 1671 | [[package]] 1672 | name = "serde_derive" 1673 | version = "1.0.209" 1674 | source = "registry+https://github.com/rust-lang/crates.io-index" 1675 | checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" 1676 | dependencies = [ 1677 | "proc-macro2", 1678 | "quote", 1679 | "syn 2.0.77", 1680 | ] 1681 | 1682 | [[package]] 1683 | name = "serde_json" 1684 | version = "1.0.127" 1685 | source = "registry+https://github.com/rust-lang/crates.io-index" 1686 | checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" 1687 | dependencies = [ 1688 | "itoa", 1689 | "memchr", 1690 | "ryu", 1691 | "serde", 1692 | ] 1693 | 1694 | [[package]] 1695 | name = "serde_path_to_error" 1696 | version = "0.1.16" 1697 | source = "registry+https://github.com/rust-lang/crates.io-index" 1698 | checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" 1699 | dependencies = [ 1700 | "itoa", 1701 | "serde", 1702 | ] 1703 | 1704 | [[package]] 1705 | name = "serde_spanned" 1706 | version = "0.6.7" 1707 | source = "registry+https://github.com/rust-lang/crates.io-index" 1708 | checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" 1709 | dependencies = [ 1710 | "serde", 1711 | ] 1712 | 1713 | [[package]] 1714 | name = "serde_urlencoded" 1715 | version = "0.7.1" 1716 | source = "registry+https://github.com/rust-lang/crates.io-index" 1717 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1718 | dependencies = [ 1719 | "form_urlencoded", 1720 | "itoa", 1721 | "ryu", 1722 | "serde", 1723 | ] 1724 | 1725 | [[package]] 1726 | name = "sha1" 1727 | version = "0.10.6" 1728 | source = "registry+https://github.com/rust-lang/crates.io-index" 1729 | checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1730 | dependencies = [ 1731 | "cfg-if", 1732 | "cpufeatures", 1733 | "digest", 1734 | ] 1735 | 1736 | [[package]] 1737 | name = "sha2" 1738 | version = "0.10.8" 1739 | source = "registry+https://github.com/rust-lang/crates.io-index" 1740 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 1741 | dependencies = [ 1742 | "cfg-if", 1743 | "cpufeatures", 1744 | "digest", 1745 | ] 1746 | 1747 | [[package]] 1748 | name = "sharded-slab" 1749 | version = "0.1.7" 1750 | source = "registry+https://github.com/rust-lang/crates.io-index" 1751 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1752 | dependencies = [ 1753 | "lazy_static", 1754 | ] 1755 | 1756 | [[package]] 1757 | name = "shellexpand" 1758 | version = "2.1.2" 1759 | source = "registry+https://github.com/rust-lang/crates.io-index" 1760 | checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" 1761 | dependencies = [ 1762 | "dirs", 1763 | ] 1764 | 1765 | [[package]] 1766 | name = "shlex" 1767 | version = "1.3.0" 1768 | source = "registry+https://github.com/rust-lang/crates.io-index" 1769 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1770 | 1771 | [[package]] 1772 | name = "slab" 1773 | version = "0.4.9" 1774 | source = "registry+https://github.com/rust-lang/crates.io-index" 1775 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1776 | dependencies = [ 1777 | "autocfg", 1778 | ] 1779 | 1780 | [[package]] 1781 | name = "slice-group-by" 1782 | version = "0.3.1" 1783 | source = "registry+https://github.com/rust-lang/crates.io-index" 1784 | checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" 1785 | 1786 | [[package]] 1787 | name = "smallvec" 1788 | version = "1.13.2" 1789 | source = "registry+https://github.com/rust-lang/crates.io-index" 1790 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1791 | dependencies = [ 1792 | "serde", 1793 | ] 1794 | 1795 | [[package]] 1796 | name = "socket2" 1797 | version = "0.5.7" 1798 | source = "registry+https://github.com/rust-lang/crates.io-index" 1799 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 1800 | dependencies = [ 1801 | "libc", 1802 | "windows-sys 0.52.0", 1803 | ] 1804 | 1805 | [[package]] 1806 | name = "sptr" 1807 | version = "0.3.2" 1808 | source = "registry+https://github.com/rust-lang/crates.io-index" 1809 | checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" 1810 | 1811 | [[package]] 1812 | name = "stable_deref_trait" 1813 | version = "1.2.0" 1814 | source = "registry+https://github.com/rust-lang/crates.io-index" 1815 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1816 | 1817 | [[package]] 1818 | name = "stateroom" 1819 | version = "0.4.4" 1820 | dependencies = [ 1821 | "serde", 1822 | ] 1823 | 1824 | [[package]] 1825 | name = "stateroom-cli" 1826 | version = "0.4.4" 1827 | dependencies = [ 1828 | "anyhow", 1829 | "cargo_metadata", 1830 | "clap", 1831 | "fs_extra", 1832 | "serde", 1833 | "stateroom", 1834 | "stateroom-server", 1835 | "stateroom-wasm-host", 1836 | "toml", 1837 | "tracing", 1838 | "tracing-subscriber", 1839 | "wasm-bindgen-cli-support", 1840 | ] 1841 | 1842 | [[package]] 1843 | name = "stateroom-server" 1844 | version = "0.4.4" 1845 | dependencies = [ 1846 | "axum", 1847 | "dashmap", 1848 | "futures-util", 1849 | "stateroom", 1850 | "tokio", 1851 | "tower-http", 1852 | "tracing", 1853 | ] 1854 | 1855 | [[package]] 1856 | name = "stateroom-wasm" 1857 | version = "0.4.4" 1858 | dependencies = [ 1859 | "bincode", 1860 | "stateroom", 1861 | "stateroom-wasm-macro", 1862 | ] 1863 | 1864 | [[package]] 1865 | name = "stateroom-wasm-host" 1866 | version = "0.4.4" 1867 | dependencies = [ 1868 | "anyhow", 1869 | "bincode", 1870 | "byteorder", 1871 | "stateroom", 1872 | "tracing", 1873 | "wasi-common", 1874 | "wasmtime", 1875 | ] 1876 | 1877 | [[package]] 1878 | name = "stateroom-wasm-macro" 1879 | version = "0.4.0" 1880 | dependencies = [ 1881 | "proc-macro2", 1882 | "quote", 1883 | "syn 2.0.77", 1884 | ] 1885 | 1886 | [[package]] 1887 | name = "strsim" 1888 | version = "0.11.1" 1889 | source = "registry+https://github.com/rust-lang/crates.io-index" 1890 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1891 | 1892 | [[package]] 1893 | name = "syn" 1894 | version = "1.0.109" 1895 | source = "registry+https://github.com/rust-lang/crates.io-index" 1896 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1897 | dependencies = [ 1898 | "proc-macro2", 1899 | "quote", 1900 | "unicode-ident", 1901 | ] 1902 | 1903 | [[package]] 1904 | name = "syn" 1905 | version = "2.0.77" 1906 | source = "registry+https://github.com/rust-lang/crates.io-index" 1907 | checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" 1908 | dependencies = [ 1909 | "proc-macro2", 1910 | "quote", 1911 | "unicode-ident", 1912 | ] 1913 | 1914 | [[package]] 1915 | name = "sync_wrapper" 1916 | version = "0.1.2" 1917 | source = "registry+https://github.com/rust-lang/crates.io-index" 1918 | checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" 1919 | 1920 | [[package]] 1921 | name = "sync_wrapper" 1922 | version = "1.0.1" 1923 | source = "registry+https://github.com/rust-lang/crates.io-index" 1924 | checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" 1925 | 1926 | [[package]] 1927 | name = "system-interface" 1928 | version = "0.27.2" 1929 | source = "registry+https://github.com/rust-lang/crates.io-index" 1930 | checksum = "b858526d22750088a9b3cf2e3c2aacebd5377f13adeec02860c30d09113010a6" 1931 | dependencies = [ 1932 | "bitflags", 1933 | "cap-fs-ext", 1934 | "cap-std", 1935 | "fd-lock", 1936 | "io-lifetimes", 1937 | "rustix", 1938 | "windows-sys 0.52.0", 1939 | "winx", 1940 | ] 1941 | 1942 | [[package]] 1943 | name = "target-lexicon" 1944 | version = "0.12.16" 1945 | source = "registry+https://github.com/rust-lang/crates.io-index" 1946 | checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" 1947 | 1948 | [[package]] 1949 | name = "tempfile" 1950 | version = "3.12.0" 1951 | source = "registry+https://github.com/rust-lang/crates.io-index" 1952 | checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" 1953 | dependencies = [ 1954 | "cfg-if", 1955 | "fastrand", 1956 | "once_cell", 1957 | "rustix", 1958 | "windows-sys 0.59.0", 1959 | ] 1960 | 1961 | [[package]] 1962 | name = "termcolor" 1963 | version = "1.4.1" 1964 | source = "registry+https://github.com/rust-lang/crates.io-index" 1965 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 1966 | dependencies = [ 1967 | "winapi-util", 1968 | ] 1969 | 1970 | [[package]] 1971 | name = "thiserror" 1972 | version = "1.0.63" 1973 | source = "registry+https://github.com/rust-lang/crates.io-index" 1974 | checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" 1975 | dependencies = [ 1976 | "thiserror-impl", 1977 | ] 1978 | 1979 | [[package]] 1980 | name = "thiserror-impl" 1981 | version = "1.0.63" 1982 | source = "registry+https://github.com/rust-lang/crates.io-index" 1983 | checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" 1984 | dependencies = [ 1985 | "proc-macro2", 1986 | "quote", 1987 | "syn 2.0.77", 1988 | ] 1989 | 1990 | [[package]] 1991 | name = "thread_local" 1992 | version = "1.1.8" 1993 | source = "registry+https://github.com/rust-lang/crates.io-index" 1994 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 1995 | dependencies = [ 1996 | "cfg-if", 1997 | "once_cell", 1998 | ] 1999 | 2000 | [[package]] 2001 | name = "tinyvec" 2002 | version = "1.8.0" 2003 | source = "registry+https://github.com/rust-lang/crates.io-index" 2004 | checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" 2005 | dependencies = [ 2006 | "tinyvec_macros", 2007 | ] 2008 | 2009 | [[package]] 2010 | name = "tinyvec_macros" 2011 | version = "0.1.1" 2012 | source = "registry+https://github.com/rust-lang/crates.io-index" 2013 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 2014 | 2015 | [[package]] 2016 | name = "tokio" 2017 | version = "1.40.0" 2018 | source = "registry+https://github.com/rust-lang/crates.io-index" 2019 | checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" 2020 | dependencies = [ 2021 | "backtrace", 2022 | "bytes", 2023 | "libc", 2024 | "mio", 2025 | "pin-project-lite", 2026 | "socket2", 2027 | "tokio-macros", 2028 | "windows-sys 0.52.0", 2029 | ] 2030 | 2031 | [[package]] 2032 | name = "tokio-macros" 2033 | version = "2.4.0" 2034 | source = "registry+https://github.com/rust-lang/crates.io-index" 2035 | checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" 2036 | dependencies = [ 2037 | "proc-macro2", 2038 | "quote", 2039 | "syn 2.0.77", 2040 | ] 2041 | 2042 | [[package]] 2043 | name = "tokio-tungstenite" 2044 | version = "0.21.0" 2045 | source = "registry+https://github.com/rust-lang/crates.io-index" 2046 | checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" 2047 | dependencies = [ 2048 | "futures-util", 2049 | "log", 2050 | "tokio", 2051 | "tungstenite", 2052 | ] 2053 | 2054 | [[package]] 2055 | name = "tokio-util" 2056 | version = "0.7.11" 2057 | source = "registry+https://github.com/rust-lang/crates.io-index" 2058 | checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" 2059 | dependencies = [ 2060 | "bytes", 2061 | "futures-core", 2062 | "futures-sink", 2063 | "pin-project-lite", 2064 | "tokio", 2065 | ] 2066 | 2067 | [[package]] 2068 | name = "toml" 2069 | version = "0.8.19" 2070 | source = "registry+https://github.com/rust-lang/crates.io-index" 2071 | checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" 2072 | dependencies = [ 2073 | "serde", 2074 | "serde_spanned", 2075 | "toml_datetime", 2076 | "toml_edit", 2077 | ] 2078 | 2079 | [[package]] 2080 | name = "toml_datetime" 2081 | version = "0.6.8" 2082 | source = "registry+https://github.com/rust-lang/crates.io-index" 2083 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 2084 | dependencies = [ 2085 | "serde", 2086 | ] 2087 | 2088 | [[package]] 2089 | name = "toml_edit" 2090 | version = "0.22.20" 2091 | source = "registry+https://github.com/rust-lang/crates.io-index" 2092 | checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" 2093 | dependencies = [ 2094 | "indexmap 2.5.0", 2095 | "serde", 2096 | "serde_spanned", 2097 | "toml_datetime", 2098 | "winnow", 2099 | ] 2100 | 2101 | [[package]] 2102 | name = "tower" 2103 | version = "0.4.13" 2104 | source = "registry+https://github.com/rust-lang/crates.io-index" 2105 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" 2106 | dependencies = [ 2107 | "futures-core", 2108 | "futures-util", 2109 | "pin-project", 2110 | "pin-project-lite", 2111 | "tokio", 2112 | "tower-layer", 2113 | "tower-service", 2114 | "tracing", 2115 | ] 2116 | 2117 | [[package]] 2118 | name = "tower-http" 2119 | version = "0.5.2" 2120 | source = "registry+https://github.com/rust-lang/crates.io-index" 2121 | checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" 2122 | dependencies = [ 2123 | "bitflags", 2124 | "bytes", 2125 | "futures-util", 2126 | "http", 2127 | "http-body", 2128 | "http-body-util", 2129 | "http-range-header", 2130 | "httpdate", 2131 | "mime", 2132 | "mime_guess", 2133 | "percent-encoding", 2134 | "pin-project-lite", 2135 | "tokio", 2136 | "tokio-util", 2137 | "tower-layer", 2138 | "tower-service", 2139 | "tracing", 2140 | ] 2141 | 2142 | [[package]] 2143 | name = "tower-layer" 2144 | version = "0.3.3" 2145 | source = "registry+https://github.com/rust-lang/crates.io-index" 2146 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 2147 | 2148 | [[package]] 2149 | name = "tower-service" 2150 | version = "0.3.3" 2151 | source = "registry+https://github.com/rust-lang/crates.io-index" 2152 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 2153 | 2154 | [[package]] 2155 | name = "tracing" 2156 | version = "0.1.40" 2157 | source = "registry+https://github.com/rust-lang/crates.io-index" 2158 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 2159 | dependencies = [ 2160 | "log", 2161 | "pin-project-lite", 2162 | "tracing-attributes", 2163 | "tracing-core", 2164 | ] 2165 | 2166 | [[package]] 2167 | name = "tracing-attributes" 2168 | version = "0.1.27" 2169 | source = "registry+https://github.com/rust-lang/crates.io-index" 2170 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 2171 | dependencies = [ 2172 | "proc-macro2", 2173 | "quote", 2174 | "syn 2.0.77", 2175 | ] 2176 | 2177 | [[package]] 2178 | name = "tracing-core" 2179 | version = "0.1.32" 2180 | source = "registry+https://github.com/rust-lang/crates.io-index" 2181 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 2182 | dependencies = [ 2183 | "once_cell", 2184 | "valuable", 2185 | ] 2186 | 2187 | [[package]] 2188 | name = "tracing-log" 2189 | version = "0.2.0" 2190 | source = "registry+https://github.com/rust-lang/crates.io-index" 2191 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 2192 | dependencies = [ 2193 | "log", 2194 | "once_cell", 2195 | "tracing-core", 2196 | ] 2197 | 2198 | [[package]] 2199 | name = "tracing-subscriber" 2200 | version = "0.3.18" 2201 | source = "registry+https://github.com/rust-lang/crates.io-index" 2202 | checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" 2203 | dependencies = [ 2204 | "matchers", 2205 | "nu-ansi-term", 2206 | "once_cell", 2207 | "regex", 2208 | "sharded-slab", 2209 | "smallvec", 2210 | "thread_local", 2211 | "tracing", 2212 | "tracing-core", 2213 | "tracing-log", 2214 | ] 2215 | 2216 | [[package]] 2217 | name = "tungstenite" 2218 | version = "0.21.0" 2219 | source = "registry+https://github.com/rust-lang/crates.io-index" 2220 | checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" 2221 | dependencies = [ 2222 | "byteorder", 2223 | "bytes", 2224 | "data-encoding", 2225 | "http", 2226 | "httparse", 2227 | "log", 2228 | "rand", 2229 | "sha1", 2230 | "thiserror", 2231 | "url", 2232 | "utf-8", 2233 | ] 2234 | 2235 | [[package]] 2236 | name = "typenum" 2237 | version = "1.17.0" 2238 | source = "registry+https://github.com/rust-lang/crates.io-index" 2239 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 2240 | 2241 | [[package]] 2242 | name = "unicase" 2243 | version = "2.7.0" 2244 | source = "registry+https://github.com/rust-lang/crates.io-index" 2245 | checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" 2246 | dependencies = [ 2247 | "version_check", 2248 | ] 2249 | 2250 | [[package]] 2251 | name = "unicode-bidi" 2252 | version = "0.3.15" 2253 | source = "registry+https://github.com/rust-lang/crates.io-index" 2254 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 2255 | 2256 | [[package]] 2257 | name = "unicode-ident" 2258 | version = "1.0.12" 2259 | source = "registry+https://github.com/rust-lang/crates.io-index" 2260 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 2261 | 2262 | [[package]] 2263 | name = "unicode-normalization" 2264 | version = "0.1.23" 2265 | source = "registry+https://github.com/rust-lang/crates.io-index" 2266 | checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 2267 | dependencies = [ 2268 | "tinyvec", 2269 | ] 2270 | 2271 | [[package]] 2272 | name = "unicode-segmentation" 2273 | version = "1.11.0" 2274 | source = "registry+https://github.com/rust-lang/crates.io-index" 2275 | checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" 2276 | 2277 | [[package]] 2278 | name = "unicode-width" 2279 | version = "0.1.13" 2280 | source = "registry+https://github.com/rust-lang/crates.io-index" 2281 | checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" 2282 | 2283 | [[package]] 2284 | name = "unicode-xid" 2285 | version = "0.2.5" 2286 | source = "registry+https://github.com/rust-lang/crates.io-index" 2287 | checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" 2288 | 2289 | [[package]] 2290 | name = "url" 2291 | version = "2.5.2" 2292 | source = "registry+https://github.com/rust-lang/crates.io-index" 2293 | checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" 2294 | dependencies = [ 2295 | "form_urlencoded", 2296 | "idna", 2297 | "percent-encoding", 2298 | ] 2299 | 2300 | [[package]] 2301 | name = "utf-8" 2302 | version = "0.7.6" 2303 | source = "registry+https://github.com/rust-lang/crates.io-index" 2304 | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 2305 | 2306 | [[package]] 2307 | name = "utf8parse" 2308 | version = "0.2.2" 2309 | source = "registry+https://github.com/rust-lang/crates.io-index" 2310 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 2311 | 2312 | [[package]] 2313 | name = "uuid" 2314 | version = "1.10.0" 2315 | source = "registry+https://github.com/rust-lang/crates.io-index" 2316 | checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" 2317 | 2318 | [[package]] 2319 | name = "valuable" 2320 | version = "0.1.0" 2321 | source = "registry+https://github.com/rust-lang/crates.io-index" 2322 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 2323 | 2324 | [[package]] 2325 | name = "version_check" 2326 | version = "0.9.5" 2327 | source = "registry+https://github.com/rust-lang/crates.io-index" 2328 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2329 | 2330 | [[package]] 2331 | name = "walrus" 2332 | version = "0.21.1" 2333 | source = "registry+https://github.com/rust-lang/crates.io-index" 2334 | checksum = "467611cafbc8a84834b77d2b4bb191fd2f5769752def8340407e924390c6883b" 2335 | dependencies = [ 2336 | "anyhow", 2337 | "gimli 0.26.2", 2338 | "id-arena", 2339 | "leb128", 2340 | "log", 2341 | "walrus-macro", 2342 | "wasm-encoder 0.212.0", 2343 | "wasmparser 0.212.0", 2344 | ] 2345 | 2346 | [[package]] 2347 | name = "walrus-macro" 2348 | version = "0.19.0" 2349 | source = "registry+https://github.com/rust-lang/crates.io-index" 2350 | checksum = "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7" 2351 | dependencies = [ 2352 | "heck 0.3.3", 2353 | "proc-macro2", 2354 | "quote", 2355 | "syn 1.0.109", 2356 | ] 2357 | 2358 | [[package]] 2359 | name = "wasi" 2360 | version = "0.11.0+wasi-snapshot-preview1" 2361 | source = "registry+https://github.com/rust-lang/crates.io-index" 2362 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2363 | 2364 | [[package]] 2365 | name = "wasi-common" 2366 | version = "24.0.0" 2367 | source = "registry+https://github.com/rust-lang/crates.io-index" 2368 | checksum = "7336747832c6fe1086c81ef38b63dfeaeec48fc1b7c33a88fd16115cc940d178" 2369 | dependencies = [ 2370 | "anyhow", 2371 | "bitflags", 2372 | "cap-fs-ext", 2373 | "cap-rand", 2374 | "cap-std", 2375 | "cap-time-ext", 2376 | "fs-set-times", 2377 | "io-extras", 2378 | "io-lifetimes", 2379 | "log", 2380 | "once_cell", 2381 | "rustix", 2382 | "system-interface", 2383 | "thiserror", 2384 | "tracing", 2385 | "wasmtime", 2386 | "wiggle", 2387 | "windows-sys 0.52.0", 2388 | ] 2389 | 2390 | [[package]] 2391 | name = "wasm-bindgen" 2392 | version = "0.2.93" 2393 | source = "registry+https://github.com/rust-lang/crates.io-index" 2394 | checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" 2395 | dependencies = [ 2396 | "cfg-if", 2397 | "once_cell", 2398 | "wasm-bindgen-macro", 2399 | ] 2400 | 2401 | [[package]] 2402 | name = "wasm-bindgen-backend" 2403 | version = "0.2.93" 2404 | source = "registry+https://github.com/rust-lang/crates.io-index" 2405 | checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" 2406 | dependencies = [ 2407 | "bumpalo", 2408 | "log", 2409 | "once_cell", 2410 | "proc-macro2", 2411 | "quote", 2412 | "syn 2.0.77", 2413 | "wasm-bindgen-shared", 2414 | ] 2415 | 2416 | [[package]] 2417 | name = "wasm-bindgen-cli-support" 2418 | version = "0.2.93" 2419 | source = "registry+https://github.com/rust-lang/crates.io-index" 2420 | checksum = "1a7f49ca6e7da74d53d6d716b868828a47d1c3808a8f676e2104fadd3b9874bb" 2421 | dependencies = [ 2422 | "anyhow", 2423 | "base64 0.22.1", 2424 | "log", 2425 | "rustc-demangle", 2426 | "serde_json", 2427 | "tempfile", 2428 | "unicode-ident", 2429 | "walrus", 2430 | "wasm-bindgen-externref-xform", 2431 | "wasm-bindgen-multi-value-xform", 2432 | "wasm-bindgen-shared", 2433 | "wasm-bindgen-threads-xform", 2434 | "wasm-bindgen-wasm-conventions", 2435 | "wasm-bindgen-wasm-interpreter", 2436 | ] 2437 | 2438 | [[package]] 2439 | name = "wasm-bindgen-externref-xform" 2440 | version = "0.2.93" 2441 | source = "registry+https://github.com/rust-lang/crates.io-index" 2442 | checksum = "2c1f37f2705f4177cc87e5b2763115d078d39e4843e351438b34b085d53a8930" 2443 | dependencies = [ 2444 | "anyhow", 2445 | "walrus", 2446 | "wasm-bindgen-wasm-conventions", 2447 | ] 2448 | 2449 | [[package]] 2450 | name = "wasm-bindgen-macro" 2451 | version = "0.2.93" 2452 | source = "registry+https://github.com/rust-lang/crates.io-index" 2453 | checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" 2454 | dependencies = [ 2455 | "quote", 2456 | "wasm-bindgen-macro-support", 2457 | ] 2458 | 2459 | [[package]] 2460 | name = "wasm-bindgen-macro-support" 2461 | version = "0.2.93" 2462 | source = "registry+https://github.com/rust-lang/crates.io-index" 2463 | checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" 2464 | dependencies = [ 2465 | "proc-macro2", 2466 | "quote", 2467 | "syn 2.0.77", 2468 | "wasm-bindgen-backend", 2469 | "wasm-bindgen-shared", 2470 | ] 2471 | 2472 | [[package]] 2473 | name = "wasm-bindgen-multi-value-xform" 2474 | version = "0.2.93" 2475 | source = "registry+https://github.com/rust-lang/crates.io-index" 2476 | checksum = "2864e3f221fef3869992b541b238e55f53f99b43c4ce220422a6e1ec9458dc21" 2477 | dependencies = [ 2478 | "anyhow", 2479 | "walrus", 2480 | "wasm-bindgen-wasm-conventions", 2481 | ] 2482 | 2483 | [[package]] 2484 | name = "wasm-bindgen-shared" 2485 | version = "0.2.93" 2486 | source = "registry+https://github.com/rust-lang/crates.io-index" 2487 | checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" 2488 | 2489 | [[package]] 2490 | name = "wasm-bindgen-threads-xform" 2491 | version = "0.2.93" 2492 | source = "registry+https://github.com/rust-lang/crates.io-index" 2493 | checksum = "7e9ca60ee029d64cf6f63a630050935360c3877844f6de38e8287afb8f1d2715" 2494 | dependencies = [ 2495 | "anyhow", 2496 | "walrus", 2497 | "wasm-bindgen-wasm-conventions", 2498 | ] 2499 | 2500 | [[package]] 2501 | name = "wasm-bindgen-wasm-conventions" 2502 | version = "0.2.93" 2503 | source = "registry+https://github.com/rust-lang/crates.io-index" 2504 | checksum = "6e429e00149de60ffc768e6e1f0563778a237e7c11cc01801edf9734bff8161f" 2505 | dependencies = [ 2506 | "anyhow", 2507 | "leb128", 2508 | "log", 2509 | "walrus", 2510 | "wasmparser 0.212.0", 2511 | ] 2512 | 2513 | [[package]] 2514 | name = "wasm-bindgen-wasm-interpreter" 2515 | version = "0.2.93" 2516 | source = "registry+https://github.com/rust-lang/crates.io-index" 2517 | checksum = "771c49db324f195f221796bf3868247dd91cca4a85604dcb3729213e439abe59" 2518 | dependencies = [ 2519 | "anyhow", 2520 | "log", 2521 | "walrus", 2522 | "wasm-bindgen-wasm-conventions", 2523 | ] 2524 | 2525 | [[package]] 2526 | name = "wasm-encoder" 2527 | version = "0.212.0" 2528 | source = "registry+https://github.com/rust-lang/crates.io-index" 2529 | checksum = "501940df4418b8929eb6d52f1aade1fdd15a5b86c92453cb696e3c906bd3fc33" 2530 | dependencies = [ 2531 | "leb128", 2532 | ] 2533 | 2534 | [[package]] 2535 | name = "wasm-encoder" 2536 | version = "0.215.0" 2537 | source = "registry+https://github.com/rust-lang/crates.io-index" 2538 | checksum = "4fb56df3e06b8e6b77e37d2969a50ba51281029a9aeb3855e76b7f49b6418847" 2539 | dependencies = [ 2540 | "leb128", 2541 | ] 2542 | 2543 | [[package]] 2544 | name = "wasm-encoder" 2545 | version = "0.216.0" 2546 | source = "registry+https://github.com/rust-lang/crates.io-index" 2547 | checksum = "04c23aebea22c8a75833ae08ed31ccc020835b12a41999e58c31464271b94a88" 2548 | dependencies = [ 2549 | "leb128", 2550 | ] 2551 | 2552 | [[package]] 2553 | name = "wasmparser" 2554 | version = "0.212.0" 2555 | source = "registry+https://github.com/rust-lang/crates.io-index" 2556 | checksum = "8d28bc49ba1e5c5b61ffa7a2eace10820443c4b7d1c0b144109261d14570fdf8" 2557 | dependencies = [ 2558 | "ahash", 2559 | "bitflags", 2560 | "hashbrown 0.14.5", 2561 | "indexmap 2.5.0", 2562 | "semver", 2563 | "serde", 2564 | ] 2565 | 2566 | [[package]] 2567 | name = "wasmparser" 2568 | version = "0.215.0" 2569 | source = "registry+https://github.com/rust-lang/crates.io-index" 2570 | checksum = "53fbde0881f24199b81cf49b6ff8f9c145ac8eb1b7fc439adb5c099734f7d90e" 2571 | dependencies = [ 2572 | "ahash", 2573 | "bitflags", 2574 | "hashbrown 0.14.5", 2575 | "indexmap 2.5.0", 2576 | "semver", 2577 | "serde", 2578 | ] 2579 | 2580 | [[package]] 2581 | name = "wasmprinter" 2582 | version = "0.215.0" 2583 | source = "registry+https://github.com/rust-lang/crates.io-index" 2584 | checksum = "d8e9a325d85053408209b3d2ce5eaddd0dd6864d1cff7a007147ba073157defc" 2585 | dependencies = [ 2586 | "anyhow", 2587 | "termcolor", 2588 | "wasmparser 0.215.0", 2589 | ] 2590 | 2591 | [[package]] 2592 | name = "wasmtime" 2593 | version = "24.0.0" 2594 | source = "registry+https://github.com/rust-lang/crates.io-index" 2595 | checksum = "9a5883d64dfc8423c56e3d8df27cffc44db25336aa468e8e0724fddf30a333d7" 2596 | dependencies = [ 2597 | "addr2line", 2598 | "anyhow", 2599 | "async-trait", 2600 | "bitflags", 2601 | "bumpalo", 2602 | "cc", 2603 | "cfg-if", 2604 | "encoding_rs", 2605 | "fxprof-processed-profile", 2606 | "gimli 0.29.0", 2607 | "hashbrown 0.14.5", 2608 | "indexmap 2.5.0", 2609 | "ittapi", 2610 | "libc", 2611 | "libm", 2612 | "log", 2613 | "mach2", 2614 | "memfd", 2615 | "object", 2616 | "once_cell", 2617 | "paste", 2618 | "postcard", 2619 | "psm", 2620 | "rayon", 2621 | "rustix", 2622 | "semver", 2623 | "serde", 2624 | "serde_derive", 2625 | "serde_json", 2626 | "smallvec", 2627 | "sptr", 2628 | "target-lexicon", 2629 | "wasm-encoder 0.215.0", 2630 | "wasmparser 0.215.0", 2631 | "wasmtime-asm-macros", 2632 | "wasmtime-cache", 2633 | "wasmtime-component-macro", 2634 | "wasmtime-component-util", 2635 | "wasmtime-cranelift", 2636 | "wasmtime-environ", 2637 | "wasmtime-fiber", 2638 | "wasmtime-jit-debug", 2639 | "wasmtime-jit-icache-coherence", 2640 | "wasmtime-slab", 2641 | "wasmtime-versioned-export-macros", 2642 | "wasmtime-winch", 2643 | "wat", 2644 | "windows-sys 0.52.0", 2645 | ] 2646 | 2647 | [[package]] 2648 | name = "wasmtime-asm-macros" 2649 | version = "24.0.0" 2650 | source = "registry+https://github.com/rust-lang/crates.io-index" 2651 | checksum = "1c4dc7e2a379c0dd6be5b55857d14c4b277f43a9c429a9e14403eb61776ae3be" 2652 | dependencies = [ 2653 | "cfg-if", 2654 | ] 2655 | 2656 | [[package]] 2657 | name = "wasmtime-cache" 2658 | version = "24.0.0" 2659 | source = "registry+https://github.com/rust-lang/crates.io-index" 2660 | checksum = "6a5b179f263a318e08c93281ea77cbb95e2a0c8c11e99a6188b53ead77233722" 2661 | dependencies = [ 2662 | "anyhow", 2663 | "base64 0.21.7", 2664 | "directories-next", 2665 | "log", 2666 | "postcard", 2667 | "rustix", 2668 | "serde", 2669 | "serde_derive", 2670 | "sha2", 2671 | "toml", 2672 | "windows-sys 0.52.0", 2673 | "zstd", 2674 | ] 2675 | 2676 | [[package]] 2677 | name = "wasmtime-component-macro" 2678 | version = "24.0.0" 2679 | source = "registry+https://github.com/rust-lang/crates.io-index" 2680 | checksum = "4b07773d1c3dab5f014ec61316ee317aa424033e17e70a63abdf7c3a47e58fcf" 2681 | dependencies = [ 2682 | "anyhow", 2683 | "proc-macro2", 2684 | "quote", 2685 | "syn 2.0.77", 2686 | "wasmtime-component-util", 2687 | "wasmtime-wit-bindgen", 2688 | "wit-parser", 2689 | ] 2690 | 2691 | [[package]] 2692 | name = "wasmtime-component-util" 2693 | version = "24.0.0" 2694 | source = "registry+https://github.com/rust-lang/crates.io-index" 2695 | checksum = "e38d735320f4e83478369ce649ad8fe87c6b893220902e798547a225fc0c5874" 2696 | 2697 | [[package]] 2698 | name = "wasmtime-cranelift" 2699 | version = "24.0.0" 2700 | source = "registry+https://github.com/rust-lang/crates.io-index" 2701 | checksum = "e570d831d0785d93d7d8c722b1eb9a34e0d0c1534317666f65892818358a2da9" 2702 | dependencies = [ 2703 | "anyhow", 2704 | "cfg-if", 2705 | "cranelift-codegen", 2706 | "cranelift-control", 2707 | "cranelift-entity", 2708 | "cranelift-frontend", 2709 | "cranelift-native", 2710 | "cranelift-wasm", 2711 | "gimli 0.29.0", 2712 | "log", 2713 | "object", 2714 | "target-lexicon", 2715 | "thiserror", 2716 | "wasmparser 0.215.0", 2717 | "wasmtime-environ", 2718 | "wasmtime-versioned-export-macros", 2719 | ] 2720 | 2721 | [[package]] 2722 | name = "wasmtime-environ" 2723 | version = "24.0.0" 2724 | source = "registry+https://github.com/rust-lang/crates.io-index" 2725 | checksum = "c5fe80dfbd81687431a7d4f25929fae1ae96894786d5c96b14ae41164ee97377" 2726 | dependencies = [ 2727 | "anyhow", 2728 | "cpp_demangle", 2729 | "cranelift-bitset", 2730 | "cranelift-entity", 2731 | "gimli 0.29.0", 2732 | "indexmap 2.5.0", 2733 | "log", 2734 | "object", 2735 | "postcard", 2736 | "rustc-demangle", 2737 | "semver", 2738 | "serde", 2739 | "serde_derive", 2740 | "target-lexicon", 2741 | "wasm-encoder 0.215.0", 2742 | "wasmparser 0.215.0", 2743 | "wasmprinter", 2744 | "wasmtime-component-util", 2745 | "wasmtime-types", 2746 | ] 2747 | 2748 | [[package]] 2749 | name = "wasmtime-fiber" 2750 | version = "24.0.0" 2751 | source = "registry+https://github.com/rust-lang/crates.io-index" 2752 | checksum = "0f39043d13c7b58db69dc9a0feb191a961e75a9ec2402aebf42de183c022bb8a" 2753 | dependencies = [ 2754 | "anyhow", 2755 | "cc", 2756 | "cfg-if", 2757 | "rustix", 2758 | "wasmtime-asm-macros", 2759 | "wasmtime-versioned-export-macros", 2760 | "windows-sys 0.52.0", 2761 | ] 2762 | 2763 | [[package]] 2764 | name = "wasmtime-jit-debug" 2765 | version = "24.0.0" 2766 | source = "registry+https://github.com/rust-lang/crates.io-index" 2767 | checksum = "aec346412363eb26067cb6473281a45bd273cbbcafa3dc862793c946eff6ba7f" 2768 | dependencies = [ 2769 | "object", 2770 | "once_cell", 2771 | "rustix", 2772 | "wasmtime-versioned-export-macros", 2773 | ] 2774 | 2775 | [[package]] 2776 | name = "wasmtime-jit-icache-coherence" 2777 | version = "24.0.0" 2778 | source = "registry+https://github.com/rust-lang/crates.io-index" 2779 | checksum = "d15de8429db996f0d17a4163a35eccc3f874cbfb50f29c379951ea1bbb39452e" 2780 | dependencies = [ 2781 | "anyhow", 2782 | "cfg-if", 2783 | "libc", 2784 | "windows-sys 0.52.0", 2785 | ] 2786 | 2787 | [[package]] 2788 | name = "wasmtime-slab" 2789 | version = "24.0.0" 2790 | source = "registry+https://github.com/rust-lang/crates.io-index" 2791 | checksum = "1f68d38fa6b30c5e1fc7d608263062997306f79e577ebd197ddcd6b0f55d87d1" 2792 | 2793 | [[package]] 2794 | name = "wasmtime-types" 2795 | version = "24.0.0" 2796 | source = "registry+https://github.com/rust-lang/crates.io-index" 2797 | checksum = "6634e7079d9c5cfc81af8610ed59b488cc5b7f9777a2f4c1667a2565c2e45249" 2798 | dependencies = [ 2799 | "anyhow", 2800 | "cranelift-entity", 2801 | "serde", 2802 | "serde_derive", 2803 | "smallvec", 2804 | "wasmparser 0.215.0", 2805 | ] 2806 | 2807 | [[package]] 2808 | name = "wasmtime-versioned-export-macros" 2809 | version = "24.0.0" 2810 | source = "registry+https://github.com/rust-lang/crates.io-index" 2811 | checksum = "3850e3511d6c7f11a72d571890b0ed5f6204681f7f050b9de2690e7f13123fed" 2812 | dependencies = [ 2813 | "proc-macro2", 2814 | "quote", 2815 | "syn 2.0.77", 2816 | ] 2817 | 2818 | [[package]] 2819 | name = "wasmtime-winch" 2820 | version = "24.0.0" 2821 | source = "registry+https://github.com/rust-lang/crates.io-index" 2822 | checksum = "2a25199625effa4c13dd790d64bd56884b014c69829431bfe43991c740bd5bc1" 2823 | dependencies = [ 2824 | "anyhow", 2825 | "cranelift-codegen", 2826 | "gimli 0.29.0", 2827 | "object", 2828 | "target-lexicon", 2829 | "wasmparser 0.215.0", 2830 | "wasmtime-cranelift", 2831 | "wasmtime-environ", 2832 | "winch-codegen", 2833 | ] 2834 | 2835 | [[package]] 2836 | name = "wasmtime-wit-bindgen" 2837 | version = "24.0.0" 2838 | source = "registry+https://github.com/rust-lang/crates.io-index" 2839 | checksum = "3cb331ac7ed1d5ba49cddcdb6b11973752a857148858bb308777d2fc5584121f" 2840 | dependencies = [ 2841 | "anyhow", 2842 | "heck 0.4.1", 2843 | "indexmap 2.5.0", 2844 | "wit-parser", 2845 | ] 2846 | 2847 | [[package]] 2848 | name = "wast" 2849 | version = "35.0.2" 2850 | source = "registry+https://github.com/rust-lang/crates.io-index" 2851 | checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" 2852 | dependencies = [ 2853 | "leb128", 2854 | ] 2855 | 2856 | [[package]] 2857 | name = "wast" 2858 | version = "216.0.0" 2859 | source = "registry+https://github.com/rust-lang/crates.io-index" 2860 | checksum = "f7eb1f2eecd913fdde0dc6c3439d0f24530a98ac6db6cb3d14d92a5328554a08" 2861 | dependencies = [ 2862 | "bumpalo", 2863 | "leb128", 2864 | "memchr", 2865 | "unicode-width", 2866 | "wasm-encoder 0.216.0", 2867 | ] 2868 | 2869 | [[package]] 2870 | name = "wat" 2871 | version = "1.216.0" 2872 | source = "registry+https://github.com/rust-lang/crates.io-index" 2873 | checksum = "ac0409090fb5154f95fb5ba3235675fd9e579e731524d63b6a2f653e1280c82a" 2874 | dependencies = [ 2875 | "wast 216.0.0", 2876 | ] 2877 | 2878 | [[package]] 2879 | name = "wiggle" 2880 | version = "24.0.0" 2881 | source = "registry+https://github.com/rust-lang/crates.io-index" 2882 | checksum = "cc850ca3c02c5835934d23f28cec4c5a3fb66fe0b4ecd968bbb35609dda5ddc0" 2883 | dependencies = [ 2884 | "anyhow", 2885 | "async-trait", 2886 | "bitflags", 2887 | "thiserror", 2888 | "tracing", 2889 | "wasmtime", 2890 | "wiggle-macro", 2891 | ] 2892 | 2893 | [[package]] 2894 | name = "wiggle-generate" 2895 | version = "24.0.0" 2896 | source = "registry+https://github.com/rust-lang/crates.io-index" 2897 | checksum = "634b8804a67200bcb43ea8af5f7c53e862439a086b68b16fd333454bc74d5aab" 2898 | dependencies = [ 2899 | "anyhow", 2900 | "heck 0.4.1", 2901 | "proc-macro2", 2902 | "quote", 2903 | "shellexpand", 2904 | "syn 2.0.77", 2905 | "witx", 2906 | ] 2907 | 2908 | [[package]] 2909 | name = "wiggle-macro" 2910 | version = "24.0.0" 2911 | source = "registry+https://github.com/rust-lang/crates.io-index" 2912 | checksum = "474b7cbdb942c74031e619d66c600bba7f73867c5800fc2c2306cf307649be2f" 2913 | dependencies = [ 2914 | "proc-macro2", 2915 | "quote", 2916 | "syn 2.0.77", 2917 | "wiggle-generate", 2918 | ] 2919 | 2920 | [[package]] 2921 | name = "winapi" 2922 | version = "0.3.9" 2923 | source = "registry+https://github.com/rust-lang/crates.io-index" 2924 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2925 | dependencies = [ 2926 | "winapi-i686-pc-windows-gnu", 2927 | "winapi-x86_64-pc-windows-gnu", 2928 | ] 2929 | 2930 | [[package]] 2931 | name = "winapi-i686-pc-windows-gnu" 2932 | version = "0.4.0" 2933 | source = "registry+https://github.com/rust-lang/crates.io-index" 2934 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2935 | 2936 | [[package]] 2937 | name = "winapi-util" 2938 | version = "0.1.9" 2939 | source = "registry+https://github.com/rust-lang/crates.io-index" 2940 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 2941 | dependencies = [ 2942 | "windows-sys 0.59.0", 2943 | ] 2944 | 2945 | [[package]] 2946 | name = "winapi-x86_64-pc-windows-gnu" 2947 | version = "0.4.0" 2948 | source = "registry+https://github.com/rust-lang/crates.io-index" 2949 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2950 | 2951 | [[package]] 2952 | name = "winch-codegen" 2953 | version = "0.22.0" 2954 | source = "registry+https://github.com/rust-lang/crates.io-index" 2955 | checksum = "073efe897d9ead7fc609874f94580afc831114af5149b6a90ee0a3a39b497fe0" 2956 | dependencies = [ 2957 | "anyhow", 2958 | "cranelift-codegen", 2959 | "gimli 0.29.0", 2960 | "regalloc2", 2961 | "smallvec", 2962 | "target-lexicon", 2963 | "wasmparser 0.215.0", 2964 | "wasmtime-cranelift", 2965 | "wasmtime-environ", 2966 | ] 2967 | 2968 | [[package]] 2969 | name = "windows-core" 2970 | version = "0.52.0" 2971 | source = "registry+https://github.com/rust-lang/crates.io-index" 2972 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 2973 | dependencies = [ 2974 | "windows-targets", 2975 | ] 2976 | 2977 | [[package]] 2978 | name = "windows-sys" 2979 | version = "0.52.0" 2980 | source = "registry+https://github.com/rust-lang/crates.io-index" 2981 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2982 | dependencies = [ 2983 | "windows-targets", 2984 | ] 2985 | 2986 | [[package]] 2987 | name = "windows-sys" 2988 | version = "0.59.0" 2989 | source = "registry+https://github.com/rust-lang/crates.io-index" 2990 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2991 | dependencies = [ 2992 | "windows-targets", 2993 | ] 2994 | 2995 | [[package]] 2996 | name = "windows-targets" 2997 | version = "0.52.6" 2998 | source = "registry+https://github.com/rust-lang/crates.io-index" 2999 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 3000 | dependencies = [ 3001 | "windows_aarch64_gnullvm", 3002 | "windows_aarch64_msvc", 3003 | "windows_i686_gnu", 3004 | "windows_i686_gnullvm", 3005 | "windows_i686_msvc", 3006 | "windows_x86_64_gnu", 3007 | "windows_x86_64_gnullvm", 3008 | "windows_x86_64_msvc", 3009 | ] 3010 | 3011 | [[package]] 3012 | name = "windows_aarch64_gnullvm" 3013 | version = "0.52.6" 3014 | source = "registry+https://github.com/rust-lang/crates.io-index" 3015 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 3016 | 3017 | [[package]] 3018 | name = "windows_aarch64_msvc" 3019 | version = "0.52.6" 3020 | source = "registry+https://github.com/rust-lang/crates.io-index" 3021 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 3022 | 3023 | [[package]] 3024 | name = "windows_i686_gnu" 3025 | version = "0.52.6" 3026 | source = "registry+https://github.com/rust-lang/crates.io-index" 3027 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 3028 | 3029 | [[package]] 3030 | name = "windows_i686_gnullvm" 3031 | version = "0.52.6" 3032 | source = "registry+https://github.com/rust-lang/crates.io-index" 3033 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 3034 | 3035 | [[package]] 3036 | name = "windows_i686_msvc" 3037 | version = "0.52.6" 3038 | source = "registry+https://github.com/rust-lang/crates.io-index" 3039 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 3040 | 3041 | [[package]] 3042 | name = "windows_x86_64_gnu" 3043 | version = "0.52.6" 3044 | source = "registry+https://github.com/rust-lang/crates.io-index" 3045 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 3046 | 3047 | [[package]] 3048 | name = "windows_x86_64_gnullvm" 3049 | version = "0.52.6" 3050 | source = "registry+https://github.com/rust-lang/crates.io-index" 3051 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 3052 | 3053 | [[package]] 3054 | name = "windows_x86_64_msvc" 3055 | version = "0.52.6" 3056 | source = "registry+https://github.com/rust-lang/crates.io-index" 3057 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 3058 | 3059 | [[package]] 3060 | name = "winnow" 3061 | version = "0.6.18" 3062 | source = "registry+https://github.com/rust-lang/crates.io-index" 3063 | checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" 3064 | dependencies = [ 3065 | "memchr", 3066 | ] 3067 | 3068 | [[package]] 3069 | name = "winx" 3070 | version = "0.36.3" 3071 | source = "registry+https://github.com/rust-lang/crates.io-index" 3072 | checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" 3073 | dependencies = [ 3074 | "bitflags", 3075 | "windows-sys 0.52.0", 3076 | ] 3077 | 3078 | [[package]] 3079 | name = "wit-parser" 3080 | version = "0.215.0" 3081 | source = "registry+https://github.com/rust-lang/crates.io-index" 3082 | checksum = "935a97eaffd57c3b413aa510f8f0b550a4a9fe7d59e79cd8b89a83dcb860321f" 3083 | dependencies = [ 3084 | "anyhow", 3085 | "id-arena", 3086 | "indexmap 2.5.0", 3087 | "log", 3088 | "semver", 3089 | "serde", 3090 | "serde_derive", 3091 | "serde_json", 3092 | "unicode-xid", 3093 | "wasmparser 0.215.0", 3094 | ] 3095 | 3096 | [[package]] 3097 | name = "witx" 3098 | version = "0.9.1" 3099 | source = "registry+https://github.com/rust-lang/crates.io-index" 3100 | checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" 3101 | dependencies = [ 3102 | "anyhow", 3103 | "log", 3104 | "thiserror", 3105 | "wast 35.0.2", 3106 | ] 3107 | 3108 | [[package]] 3109 | name = "zerocopy" 3110 | version = "0.7.35" 3111 | source = "registry+https://github.com/rust-lang/crates.io-index" 3112 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 3113 | dependencies = [ 3114 | "byteorder", 3115 | "zerocopy-derive", 3116 | ] 3117 | 3118 | [[package]] 3119 | name = "zerocopy-derive" 3120 | version = "0.7.35" 3121 | source = "registry+https://github.com/rust-lang/crates.io-index" 3122 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 3123 | dependencies = [ 3124 | "proc-macro2", 3125 | "quote", 3126 | "syn 2.0.77", 3127 | ] 3128 | 3129 | [[package]] 3130 | name = "zstd" 3131 | version = "0.13.2" 3132 | source = "registry+https://github.com/rust-lang/crates.io-index" 3133 | checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" 3134 | dependencies = [ 3135 | "zstd-safe", 3136 | ] 3137 | 3138 | [[package]] 3139 | name = "zstd-safe" 3140 | version = "7.2.1" 3141 | source = "registry+https://github.com/rust-lang/crates.io-index" 3142 | checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" 3143 | dependencies = [ 3144 | "zstd-sys", 3145 | ] 3146 | 3147 | [[package]] 3148 | name = "zstd-sys" 3149 | version = "2.0.13+zstd.1.5.6" 3150 | source = "registry+https://github.com/rust-lang/crates.io-index" 3151 | checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" 3152 | dependencies = [ 3153 | "cc", 3154 | "pkg-config", 3155 | ] 3156 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | exclude = [ 5 | "examples", 6 | ] 7 | 8 | members = [ 9 | "stateroom", 10 | "stateroom-*", 11 | ] 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.80-slim as build 2 | 3 | WORKDIR /work 4 | COPY . . 5 | RUN cargo build -p stateroom-cli --release 6 | 7 | FROM debian:bookworm-slim 8 | 9 | COPY --from=build /work/target/release/stateroom /stateroom 10 | ENTRYPOINT [ "/stateroom" ] 11 | CMD ["serve", "/dist"] 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Paul Butler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stateroom 2 | 3 | [![crates.io](https://img.shields.io/crates/v/stateroom.svg)](https://crates.io/crates/stateroom) 4 | [![docs.rs](https://img.shields.io/badge/docs-release-brightgreen)](https://docs.rs/stateroom/0.1.0/stateroom/) 5 | [![wokflow state](https://github.com/drifting-in-space/stateroom/actions/workflows/test.yml/badge.svg)](https://github.com/drifting-in-space/stateroom/actions/workflows/test.yml) 6 | 7 | Stateroom is a minimalist framework for building lightweight, single-threaded services that send and 8 | receive messages through [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API). 9 | 10 | Services can either be native Rust code that runs in the server process, or be compiled into 11 | [WebAssembly](https://webassembly.org/) modules and loaded dynamically. 12 | 13 | ## Usage 14 | 15 | To create a Stateroom service, implement the `SimpleStateroomService` trait. There's only one function that you *must* implement, the constructor `new`. 16 | 17 | Let's implement a simple shared counter. Any connected client will be able to increment or decrement it by sending 18 | `increment` or `decrement` messages (other messages will be ignored). Whenever the value is changed, we'll broadcast it 19 | to every connected client. 20 | 21 | ```rust 22 | use stateroom_wasm::*; 23 | 24 | #[stateroom_wasm] 25 | #[derive(Default)] 26 | struct EchoServer; 27 | 28 | impl StateroomService for EchoServer { 29 | fn connect(&mut self, client_id: ClientId, ctx: &impl StateroomContext) { 30 | ctx.send_message(client_id, format!("User {:?} connected.", client_id)); 31 | } 32 | 33 | fn message(&mut self, client_id: ClientId, message: MessagePayload, ctx: &impl StateroomContext) { 34 | let Some(message) = message.text() else { 35 | return; 36 | }; 37 | 38 | ctx.send_message( 39 | MessageRecipient::Broadcast, 40 | format!("User {:?} sent '{}'", client_id, message), 41 | ); 42 | } 43 | 44 | fn disconnect(&mut self, client_id: ClientId, ctx: &impl StateroomContext) { 45 | ctx.send_message( 46 | MessageRecipient::Broadcast, 47 | format!("User {:?} left.", client_id), 48 | ); 49 | } 50 | } 51 | ``` 52 | 53 | To serve this service, we will compile it into a WebAssembly module. We import the `#[stateroom_wasm]` 54 | annotation macro and apply it to the existing `SharedCounter` declaration. 55 | 56 | ```rust 57 | use stateroom_wasm::*; 58 | 59 | #[stateroom_wasm] 60 | #[derive(Default)] 61 | struct SharedCounter(i32); 62 | 63 | impl StateroomService for SharedCounter {} 64 | ``` 65 | 66 | Then, install the `stateroom` command-line tool and the `wasm32-wasi` target, and run 67 | `stateroom dev`: 68 | 69 | ```bash 70 | $ cargo install stateroom-cli 71 | $ rustup target add wasm32-wasi 72 | $ stateroom dev 73 | ``` 74 | 75 | `stateroom dev` will build your app and serve it on port `:8080`. Then, open 76 | `http://localhost:8080/status` in your browser -- if all went well, you should see the 77 | status message `ok`. Open up developer tools in your browser and type: 78 | 79 | ```javascript 80 | let ws = new WebSocket('ws://localhost:8080/ws'); 81 | ws.onmessage = (c) => console.log(c.data); 82 | ``` 83 | 84 | This connects to your service, creating a new room with the id `1` if one doesn't exist 85 | (under default server settings, any string is a vaild room ID and connecting to a non-existant 86 | room will create it). 87 | 88 | Now, you can increment the counter by sending the `increment` message using the `ws` handle: 89 | 90 | ```javascript 91 | ws.send('increment') 92 | ``` 93 | 94 | If everything is set up correctly, the result will be printed out: 95 | 96 | ```text 97 | new value: 1 98 | ``` 99 | 100 | If multiple clients are connected, each one will receive this message. Just like that, we have a mechanism for sharing some (very basic) application state between clients. 101 | 102 | ## Modules 103 | 104 | Stateroom has a modular architecture. If all you want to do is generate a Stateroom service to 105 | be served with an existing Stateroom WebAssembly server, the main crates you will interact with 106 | will probably be [`stateroom-cli`](/stateroom-cli), which provides a command-line tool, and 107 | [`stateroom-wasm`](/stateroom-wasm), the main Cargo dependency for building services. 108 | 109 | - [`stateroom`](https://docs.rs/stateroom/) is the core, minimal implementation of the service interface. 110 | - [`stateroom-cli`](https://docs.rs/stateroom-cli/) is a command-line interface for interacting with WebAssembly-compiled Stateroom services. 111 | - [`stateroom-server`](https://docs.rs/stateroom-server/) provides an [Axum](https://github.com/tokio-rs/axum)-based WebSocket server that runs a Stateroom service. 112 | - [`stateroom-wasm`](https://docs.rs/stateroom-wasm/) provides a macro for generating WebAssembly modules from Stateroom services. 113 | - [`stateroom-wasm-host`](https://docs.rs/stateroom-wasm-host/) provides a way to import Stateroom services from WebAssembly modules. 114 | 115 | ## See Also 116 | 117 | [Aper](https://github.com/aper-dev/aper) is a state synchronization library which 118 | works with Stateroom. 119 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | out.wasm 2 | out.wat 3 | -------------------------------------------------------------------------------- /examples/binary-echo/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-wasi" 3 | -------------------------------------------------------------------------------- /examples/binary-echo/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 = "binary-echo" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "stateroom-wasm", 10 | ] 11 | 12 | [[package]] 13 | name = "bincode" 14 | version = "1.3.3" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 17 | dependencies = [ 18 | "serde", 19 | ] 20 | 21 | [[package]] 22 | name = "proc-macro2" 23 | version = "1.0.81" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" 26 | dependencies = [ 27 | "unicode-ident", 28 | ] 29 | 30 | [[package]] 31 | name = "quote" 32 | version = "1.0.36" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 35 | dependencies = [ 36 | "proc-macro2", 37 | ] 38 | 39 | [[package]] 40 | name = "serde" 41 | version = "1.0.200" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" 44 | dependencies = [ 45 | "serde_derive", 46 | ] 47 | 48 | [[package]] 49 | name = "serde_derive" 50 | version = "1.0.200" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" 53 | dependencies = [ 54 | "proc-macro2", 55 | "quote", 56 | "syn", 57 | ] 58 | 59 | [[package]] 60 | name = "stateroom" 61 | version = "0.2.8" 62 | dependencies = [ 63 | "serde", 64 | ] 65 | 66 | [[package]] 67 | name = "stateroom-wasm" 68 | version = "0.2.9" 69 | dependencies = [ 70 | "bincode", 71 | "stateroom", 72 | "stateroom-wasm-macro", 73 | ] 74 | 75 | [[package]] 76 | name = "stateroom-wasm-macro" 77 | version = "0.2.9" 78 | dependencies = [ 79 | "proc-macro2", 80 | "quote", 81 | "syn", 82 | ] 83 | 84 | [[package]] 85 | name = "syn" 86 | version = "2.0.60" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" 89 | dependencies = [ 90 | "proc-macro2", 91 | "quote", 92 | "unicode-ident", 93 | ] 94 | 95 | [[package]] 96 | name = "unicode-ident" 97 | version = "1.0.12" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 100 | -------------------------------------------------------------------------------- /examples/binary-echo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "binary-echo" 3 | version = "0.1.0" 4 | authors = ["Paul Butler "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [dependencies] 11 | stateroom-wasm = {path="../../stateroom-wasm"} 12 | -------------------------------------------------------------------------------- /examples/binary-echo/src/lib.rs: -------------------------------------------------------------------------------- 1 | use stateroom_wasm::*; 2 | 3 | #[stateroom_wasm] 4 | #[derive(Default)] 5 | struct BinaryEcho; 6 | 7 | impl StateroomService for BinaryEcho { 8 | fn message(&mut self, _: ClientId, message: MessagePayload, ctx: &impl StateroomContext) { 9 | if let Some(message) = message.text() { 10 | ctx.send_message(MessageRecipient::Broadcast, message); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/clock/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-wasi" 3 | -------------------------------------------------------------------------------- /examples/clock/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 = "bincode" 7 | version = "1.3.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 10 | dependencies = [ 11 | "serde", 12 | ] 13 | 14 | [[package]] 15 | name = "clock" 16 | version = "0.1.0" 17 | dependencies = [ 18 | "stateroom-wasm", 19 | ] 20 | 21 | [[package]] 22 | name = "proc-macro2" 23 | version = "1.0.81" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" 26 | dependencies = [ 27 | "unicode-ident", 28 | ] 29 | 30 | [[package]] 31 | name = "quote" 32 | version = "1.0.36" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 35 | dependencies = [ 36 | "proc-macro2", 37 | ] 38 | 39 | [[package]] 40 | name = "serde" 41 | version = "1.0.200" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" 44 | dependencies = [ 45 | "serde_derive", 46 | ] 47 | 48 | [[package]] 49 | name = "serde_derive" 50 | version = "1.0.200" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" 53 | dependencies = [ 54 | "proc-macro2", 55 | "quote", 56 | "syn", 57 | ] 58 | 59 | [[package]] 60 | name = "stateroom" 61 | version = "0.2.8" 62 | dependencies = [ 63 | "serde", 64 | ] 65 | 66 | [[package]] 67 | name = "stateroom-wasm" 68 | version = "0.2.9" 69 | dependencies = [ 70 | "bincode", 71 | "stateroom", 72 | "stateroom-wasm-macro", 73 | ] 74 | 75 | [[package]] 76 | name = "stateroom-wasm-macro" 77 | version = "0.2.9" 78 | dependencies = [ 79 | "proc-macro2", 80 | "quote", 81 | "syn", 82 | ] 83 | 84 | [[package]] 85 | name = "syn" 86 | version = "2.0.60" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" 89 | dependencies = [ 90 | "proc-macro2", 91 | "quote", 92 | "unicode-ident", 93 | ] 94 | 95 | [[package]] 96 | name = "unicode-ident" 97 | version = "1.0.12" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 100 | -------------------------------------------------------------------------------- /examples/clock/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "clock" 3 | version = "0.1.0" 4 | authors = ["Paul Butler "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [dependencies] 11 | stateroom-wasm = {path="../../stateroom-wasm"} 12 | -------------------------------------------------------------------------------- /examples/clock/src/lib.rs: -------------------------------------------------------------------------------- 1 | use stateroom_wasm::*; 2 | 3 | #[stateroom_wasm] 4 | #[derive(Default)] 5 | struct ClockServer(u32); 6 | 7 | impl StateroomService for ClockServer { 8 | fn init(&mut self, ctx: &impl StateroomContext) { 9 | ctx.set_timer(4000); 10 | } 11 | 12 | fn timer(&mut self, ctx: &impl StateroomContext) { 13 | ctx.send_message(MessageRecipient::Broadcast, format!("Timer @ {}", self.0)); 14 | self.0 += 1; 15 | ctx.set_timer(4000); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/counter-service/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-wasi" 3 | -------------------------------------------------------------------------------- /examples/counter-service/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 = "bincode" 7 | version = "1.3.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 10 | dependencies = [ 11 | "serde", 12 | ] 13 | 14 | [[package]] 15 | name = "counter" 16 | version = "0.1.0" 17 | dependencies = [ 18 | "stateroom-wasm", 19 | ] 20 | 21 | [[package]] 22 | name = "proc-macro2" 23 | version = "1.0.81" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" 26 | dependencies = [ 27 | "unicode-ident", 28 | ] 29 | 30 | [[package]] 31 | name = "quote" 32 | version = "1.0.36" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 35 | dependencies = [ 36 | "proc-macro2", 37 | ] 38 | 39 | [[package]] 40 | name = "serde" 41 | version = "1.0.200" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" 44 | dependencies = [ 45 | "serde_derive", 46 | ] 47 | 48 | [[package]] 49 | name = "serde_derive" 50 | version = "1.0.200" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" 53 | dependencies = [ 54 | "proc-macro2", 55 | "quote", 56 | "syn", 57 | ] 58 | 59 | [[package]] 60 | name = "stateroom" 61 | version = "0.2.8" 62 | dependencies = [ 63 | "serde", 64 | ] 65 | 66 | [[package]] 67 | name = "stateroom-wasm" 68 | version = "0.2.9" 69 | dependencies = [ 70 | "bincode", 71 | "stateroom", 72 | "stateroom-wasm-macro", 73 | ] 74 | 75 | [[package]] 76 | name = "stateroom-wasm-macro" 77 | version = "0.2.9" 78 | dependencies = [ 79 | "proc-macro2", 80 | "quote", 81 | "syn", 82 | ] 83 | 84 | [[package]] 85 | name = "syn" 86 | version = "2.0.60" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" 89 | dependencies = [ 90 | "proc-macro2", 91 | "quote", 92 | "unicode-ident", 93 | ] 94 | 95 | [[package]] 96 | name = "unicode-ident" 97 | version = "1.0.12" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 100 | -------------------------------------------------------------------------------- /examples/counter-service/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "counter" 3 | version = "0.1.0" 4 | authors = ["Paul Butler "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [dependencies] 11 | stateroom-wasm = {path="../../stateroom-wasm"} 12 | -------------------------------------------------------------------------------- /examples/counter-service/src/lib.rs: -------------------------------------------------------------------------------- 1 | use stateroom_wasm::*; 2 | 3 | #[stateroom_wasm] 4 | #[derive(Default)] 5 | struct SharedCounterServer(i32); 6 | 7 | impl StateroomService for SharedCounterServer { 8 | fn message(&mut self, _: ClientId, message: MessagePayload, ctx: &impl StateroomContext) { 9 | let Some(message) = message.text() else { 10 | return; 11 | }; 12 | 13 | match &message[..] { 14 | "increment" => self.0 += 1, 15 | "decrement" => self.0 -= 1, 16 | _ => (), 17 | } 18 | 19 | ctx.send_message( 20 | MessageRecipient::Broadcast, 21 | format!("new value: {}", self.0), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/cpu-hog/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-wasi" 3 | -------------------------------------------------------------------------------- /examples/cpu-hog/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 = "bincode" 7 | version = "1.3.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 10 | dependencies = [ 11 | "serde", 12 | ] 13 | 14 | [[package]] 15 | name = "cpu-hog" 16 | version = "0.1.0" 17 | dependencies = [ 18 | "stateroom-wasm", 19 | "wasi", 20 | ] 21 | 22 | [[package]] 23 | name = "proc-macro2" 24 | version = "1.0.81" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" 27 | dependencies = [ 28 | "unicode-ident", 29 | ] 30 | 31 | [[package]] 32 | name = "quote" 33 | version = "1.0.36" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 36 | dependencies = [ 37 | "proc-macro2", 38 | ] 39 | 40 | [[package]] 41 | name = "serde" 42 | version = "1.0.200" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" 45 | dependencies = [ 46 | "serde_derive", 47 | ] 48 | 49 | [[package]] 50 | name = "serde_derive" 51 | version = "1.0.200" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" 54 | dependencies = [ 55 | "proc-macro2", 56 | "quote", 57 | "syn", 58 | ] 59 | 60 | [[package]] 61 | name = "stateroom" 62 | version = "0.2.8" 63 | dependencies = [ 64 | "serde", 65 | ] 66 | 67 | [[package]] 68 | name = "stateroom-wasm" 69 | version = "0.2.9" 70 | dependencies = [ 71 | "bincode", 72 | "stateroom", 73 | "stateroom-wasm-macro", 74 | ] 75 | 76 | [[package]] 77 | name = "stateroom-wasm-macro" 78 | version = "0.2.9" 79 | dependencies = [ 80 | "proc-macro2", 81 | "quote", 82 | "syn", 83 | ] 84 | 85 | [[package]] 86 | name = "syn" 87 | version = "2.0.60" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" 90 | dependencies = [ 91 | "proc-macro2", 92 | "quote", 93 | "unicode-ident", 94 | ] 95 | 96 | [[package]] 97 | name = "unicode-ident" 98 | version = "1.0.12" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 101 | 102 | [[package]] 103 | name = "wasi" 104 | version = "0.10.2+wasi-snapshot-preview1" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 107 | -------------------------------------------------------------------------------- /examples/cpu-hog/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cpu-hog" 3 | version = "0.1.0" 4 | authors = ["Paul Butler "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [dependencies] 11 | stateroom-wasm = {path="../../stateroom-wasm"} 12 | wasi = "0.10.2" 13 | -------------------------------------------------------------------------------- /examples/cpu-hog/src/lib.rs: -------------------------------------------------------------------------------- 1 | use stateroom_wasm::*; 2 | 3 | // Seconds per nanosecond. (`wasi::clock_time_get` uses nanos.) 4 | const SECONDS: u64 = 1_000_000_000; 5 | 6 | #[stateroom_wasm] 7 | #[derive(Default)] 8 | struct CpuHog; 9 | 10 | fn get_time() -> u64 { 11 | unsafe { wasi::clock_time_get(wasi::CLOCKID_REALTIME, 0).unwrap() } 12 | } 13 | 14 | impl StateroomService for CpuHog { 15 | fn connect(&mut self, _: ClientId, ctx: &impl StateroomContext) { 16 | ctx.send_message(MessageRecipient::Broadcast, "Connected."); 17 | 18 | let init_time = get_time(); 19 | loop { 20 | let cur_time = get_time(); 21 | if init_time + 10 * SECONDS < cur_time { 22 | break; 23 | } 24 | } 25 | 26 | ctx.send_message(MessageRecipient::Broadcast, "Finished."); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/echo-server/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-wasi" 3 | -------------------------------------------------------------------------------- /examples/echo-server/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 = "bincode" 7 | version = "1.3.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 10 | dependencies = [ 11 | "serde", 12 | ] 13 | 14 | [[package]] 15 | name = "echo-server" 16 | version = "0.1.0" 17 | dependencies = [ 18 | "stateroom-wasm", 19 | ] 20 | 21 | [[package]] 22 | name = "proc-macro2" 23 | version = "1.0.81" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" 26 | dependencies = [ 27 | "unicode-ident", 28 | ] 29 | 30 | [[package]] 31 | name = "quote" 32 | version = "1.0.36" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 35 | dependencies = [ 36 | "proc-macro2", 37 | ] 38 | 39 | [[package]] 40 | name = "serde" 41 | version = "1.0.200" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" 44 | dependencies = [ 45 | "serde_derive", 46 | ] 47 | 48 | [[package]] 49 | name = "serde_derive" 50 | version = "1.0.200" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" 53 | dependencies = [ 54 | "proc-macro2", 55 | "quote", 56 | "syn", 57 | ] 58 | 59 | [[package]] 60 | name = "stateroom" 61 | version = "0.2.8" 62 | dependencies = [ 63 | "serde", 64 | ] 65 | 66 | [[package]] 67 | name = "stateroom-wasm" 68 | version = "0.2.9" 69 | dependencies = [ 70 | "bincode", 71 | "stateroom", 72 | "stateroom-wasm-macro", 73 | ] 74 | 75 | [[package]] 76 | name = "stateroom-wasm-macro" 77 | version = "0.2.9" 78 | dependencies = [ 79 | "proc-macro2", 80 | "quote", 81 | "syn", 82 | ] 83 | 84 | [[package]] 85 | name = "syn" 86 | version = "2.0.60" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" 89 | dependencies = [ 90 | "proc-macro2", 91 | "quote", 92 | "unicode-ident", 93 | ] 94 | 95 | [[package]] 96 | name = "unicode-ident" 97 | version = "1.0.12" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 100 | -------------------------------------------------------------------------------- /examples/echo-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "echo-server" 3 | version = "0.1.0" 4 | authors = ["Paul Butler "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [dependencies] 11 | stateroom-wasm = {path="../../stateroom-wasm"} 12 | -------------------------------------------------------------------------------- /examples/echo-server/dist/server.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsocket/stateroom/3f002031bfa05350c1de63df392a9797d18658e2/examples/echo-server/dist/server.wasm -------------------------------------------------------------------------------- /examples/echo-server/src/lib.rs: -------------------------------------------------------------------------------- 1 | use stateroom_wasm::*; 2 | 3 | #[stateroom_wasm] 4 | #[derive(Default)] 5 | struct EchoServer; 6 | 7 | impl StateroomService for EchoServer { 8 | fn connect(&mut self, client_id: ClientId, ctx: &impl StateroomContext) { 9 | ctx.send_message(client_id, format!("User {:?} connected.", client_id)); 10 | } 11 | 12 | fn message(&mut self, client_id: ClientId, message: MessagePayload, ctx: &impl StateroomContext) { 13 | let Some(message) = message.text() else { 14 | return; 15 | }; 16 | 17 | ctx.send_message( 18 | MessageRecipient::Broadcast, 19 | format!("User {:?} sent '{}'", client_id, message), 20 | ); 21 | } 22 | 23 | fn disconnect(&mut self, client_id: ClientId, ctx: &impl StateroomContext) { 24 | ctx.send_message( 25 | MessageRecipient::Broadcast, 26 | format!("User {:?} left.", client_id), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/randomness/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-wasi" 3 | -------------------------------------------------------------------------------- /examples/randomness/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 = "bincode" 7 | version = "1.3.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 10 | dependencies = [ 11 | "serde", 12 | ] 13 | 14 | [[package]] 15 | name = "bytemuck" 16 | version = "1.15.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" 19 | 20 | [[package]] 21 | name = "proc-macro2" 22 | version = "1.0.81" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" 25 | dependencies = [ 26 | "unicode-ident", 27 | ] 28 | 29 | [[package]] 30 | name = "quote" 31 | version = "1.0.36" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 34 | dependencies = [ 35 | "proc-macro2", 36 | ] 37 | 38 | [[package]] 39 | name = "randomness" 40 | version = "0.1.0" 41 | dependencies = [ 42 | "bytemuck", 43 | "stateroom-wasm", 44 | "wasi", 45 | ] 46 | 47 | [[package]] 48 | name = "serde" 49 | version = "1.0.200" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" 52 | dependencies = [ 53 | "serde_derive", 54 | ] 55 | 56 | [[package]] 57 | name = "serde_derive" 58 | version = "1.0.200" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" 61 | dependencies = [ 62 | "proc-macro2", 63 | "quote", 64 | "syn", 65 | ] 66 | 67 | [[package]] 68 | name = "stateroom" 69 | version = "0.2.8" 70 | dependencies = [ 71 | "serde", 72 | ] 73 | 74 | [[package]] 75 | name = "stateroom-wasm" 76 | version = "0.2.9" 77 | dependencies = [ 78 | "bincode", 79 | "stateroom", 80 | "stateroom-wasm-macro", 81 | ] 82 | 83 | [[package]] 84 | name = "stateroom-wasm-macro" 85 | version = "0.2.9" 86 | dependencies = [ 87 | "proc-macro2", 88 | "quote", 89 | "syn", 90 | ] 91 | 92 | [[package]] 93 | name = "syn" 94 | version = "2.0.60" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" 97 | dependencies = [ 98 | "proc-macro2", 99 | "quote", 100 | "unicode-ident", 101 | ] 102 | 103 | [[package]] 104 | name = "unicode-ident" 105 | version = "1.0.12" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 108 | 109 | [[package]] 110 | name = "wasi" 111 | version = "0.10.2+wasi-snapshot-preview1" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 114 | -------------------------------------------------------------------------------- /examples/randomness/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "randomness" 3 | version = "0.1.0" 4 | authors = ["Paul Butler "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [dependencies] 11 | bytemuck = "1.7.0" 12 | stateroom-wasm = {path="../../stateroom-wasm"} 13 | wasi = "0.10.2" 14 | -------------------------------------------------------------------------------- /examples/randomness/src/lib.rs: -------------------------------------------------------------------------------- 1 | use bytemuck::cast; 2 | use stateroom_wasm::*; 3 | 4 | #[stateroom_wasm] 5 | #[derive(Default)] 6 | struct RandomServer; 7 | 8 | impl StateroomService for RandomServer { 9 | fn connect(&mut self, client_id: ClientId, ctx: &impl StateroomContext) { 10 | let mut buf: [u8; 4] = [0, 0, 0, 0]; 11 | unsafe { 12 | wasi::random_get(&mut buf[0] as *mut u8, 4).unwrap(); 13 | } 14 | 15 | let num: [u32; 1] = cast(buf); 16 | 17 | ctx.send_message( 18 | client_id, 19 | format!("User {:?} connected. Random number: {}", client_id, num[0]), 20 | ); 21 | } 22 | 23 | fn message(&mut self, client_id: ClientId, message: MessagePayload, ctx: &impl StateroomContext) { 24 | let Some(message) = message.text() else { 25 | return; 26 | }; 27 | 28 | ctx.send_message( 29 | MessageRecipient::Broadcast, 30 | format!("User {:?} sent '{}'", client_id, message), 31 | ); 32 | } 33 | 34 | fn disconnect(&mut self, client_id: ClientId, ctx: &impl StateroomContext) { 35 | ctx.send_message( 36 | MessageRecipient::Broadcast, 37 | format!("User {:?} left.", client_id), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /stateroom-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stateroom-cli" 3 | version = "0.4.4" 4 | edition = "2018" 5 | readme = "README.md" 6 | repository = "https://github.com/drifting-in-space/stateroom" 7 | license = "MIT OR Apache-2.0" 8 | keywords = ["websocket"] 9 | description = "Command-line tool for developing Stateroom applications" 10 | 11 | [dependencies] 12 | anyhow = "1.0.52" 13 | cargo_metadata = "0.18.1" 14 | clap = { version = "4.1.6", features = ["derive"] } 15 | fs_extra = "1.2.0" 16 | serde = { version = "1.0.127", features = ["derive"] } 17 | stateroom = { path="../stateroom", version="0.4.0" } 18 | stateroom-server = { path="../stateroom-server", version="0.4.0" } 19 | stateroom-wasm-host = { path="../stateroom-wasm-host", version="0.4.0" } 20 | toml = "0.8.12" 21 | tracing = "0.1.28" 22 | tracing-subscriber = { version = "0.3.5", features = ["env-filter"] } 23 | wasm-bindgen-cli-support = "0.2.83" 24 | -------------------------------------------------------------------------------- /stateroom-cli/README.md: -------------------------------------------------------------------------------- 1 | # stateroom-cli 2 | 3 | This module implements a command-line interface for building and serving Stateroom 4 | services. 5 | 6 | ## Installation 7 | 8 | ```bash 9 | cargo install stateroom-cli 10 | ``` 11 | 12 | ## Commands 13 | 14 | *The `stateroom` command is new even relative to the other parts of Stateroom, so 15 | expect this list to grow over time and commands may be renamed or combined over time.* 16 | 17 | ### `stateroom dev` 18 | 19 | By default, the command `dev` will: 20 | - Build the current module as a `wasm32-wasi` target. 21 | - Locate the wasm output. 22 | - Run a local server that exposes it on port 8080. 23 | 24 | It can also be configured using a `stateroom.toml` file to serve static files 25 | and build a client-side WebAssembly module. See [`cli_opts.rs`](src/cli_opts.rs) 26 | for 27 | 28 | ### `stateroom serve` 29 | 30 | The command `serve [path/to/service.wasm]` will set up a server for an existing 31 | WebAssembly file. 32 | -------------------------------------------------------------------------------- /stateroom-cli/src/bin/stateroom.rs: -------------------------------------------------------------------------------- 1 | //! # `stateroom-cli`: a command-line interface to Stateroom 2 | 3 | use clap::Parser; 4 | use stateroom_cli::cli_opts::{Opts, SubCommand}; 5 | use stateroom_cli::{build, dev, serve}; 6 | use tracing_subscriber::EnvFilter; 7 | 8 | fn main() -> anyhow::Result<()> { 9 | let env_filter = EnvFilter::default() 10 | .add_directive("stateroom_cli=info".parse()?) 11 | .add_directive("stateroom_wasm_host=info".parse()?) 12 | .add_directive("stateroom_server=info".parse()?); 13 | 14 | tracing_subscriber::fmt().with_env_filter(env_filter).init(); 15 | 16 | let opts = Opts::parse(); 17 | 18 | match opts.subcommand { 19 | SubCommand::Serve(serve_opts) => serve(serve_opts), 20 | SubCommand::Dev { port } => dev(port), 21 | SubCommand::Build => build(), 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /stateroom-cli/src/build_util.rs: -------------------------------------------------------------------------------- 1 | use crate::config::StateroomConfig; 2 | use anyhow::{anyhow, Result}; 3 | use cargo_metadata::Message; 4 | use std::{ 5 | fs::read_to_string, 6 | path::PathBuf, 7 | process::{Command, Stdio}, 8 | }; 9 | use wasm_bindgen_cli_support::Bindgen; 10 | 11 | pub fn locate_config() -> anyhow::Result { 12 | if let Ok(r) = read_to_string("stateroom.toml") { 13 | tracing::info!("Loading config from file (stateroom.toml)"); 14 | toml::from_str(&r).map_err(|e| e.into()) 15 | } else { 16 | tracing::info!("Didn't find a stateroom.toml file in current directory, using default"); 17 | Ok(StateroomConfig::default()) 18 | } 19 | } 20 | 21 | pub fn run_cargo_build_command( 22 | package: &Option, 23 | target: &str, 24 | release: bool, 25 | ) -> Result { 26 | let mut build_command = Command::new("cargo"); 27 | build_command.stdout(Stdio::piped()); 28 | build_command.arg("build"); 29 | build_command.args(["--message-format", "json-render-diagnostics"]); 30 | 31 | if let Some(package) = package { 32 | // If package is None, build the package we are in. 33 | build_command.args(["--package", package]); 34 | } 35 | build_command.args(["--target", target]); 36 | 37 | if release { 38 | build_command.arg("--release"); 39 | } 40 | 41 | let mut build_command = build_command.spawn()?; 42 | let reader = std::io::BufReader::new( 43 | build_command 44 | .stdout 45 | .take() 46 | .ok_or_else(|| anyhow!("Could not read stdout stream."))?, 47 | ); 48 | 49 | let mut found_wasm_modules = Vec::new(); 50 | 51 | for message in cargo_metadata::Message::parse_stream(reader) { 52 | match message { 53 | // TODO: handle error when toolchain is not installed, and retry after 54 | // attempting to install toolchain. 55 | Ok(Message::CompilerArtifact(artifact)) => { 56 | for filename in artifact.filenames { 57 | if filename 58 | .extension() 59 | .map_or(false, |ext| ext.to_ascii_lowercase() == "wasm") 60 | { 61 | if filename.parent().map_or(false, |p| p.ends_with("deps")) { 62 | continue; 63 | } 64 | 65 | found_wasm_modules.push(filename); 66 | } 67 | } 68 | } 69 | Ok(Message::BuildFinished(finished)) => { 70 | if !finished.success { 71 | return Err(anyhow!("Build error.")); 72 | } 73 | } 74 | Err(e) => return Err(anyhow!("Unknown error during build: {:?}.", e)), 75 | _ => (), 76 | } 77 | } 78 | 79 | build_command 80 | .wait() 81 | .map_err(|e| anyhow!("Encountered OS error running build subprocess: {:?}", e))?; 82 | 83 | let result = match found_wasm_modules.as_slice() { 84 | [] => return Err(anyhow!("No .wasm files emitted by build.")), 85 | [a] => a, 86 | files => { 87 | return Err(anyhow!( 88 | "Multiple .wasm files emitted by build ({:?}).", 89 | files 90 | )) 91 | } 92 | }; 93 | 94 | Ok(result.into()) 95 | } 96 | 97 | pub struct BuildResult { 98 | pub server_wasm: String, 99 | pub client_wasm: Option, 100 | } 101 | 102 | pub fn do_build(config: &StateroomConfig) -> Result { 103 | tracing::info!("Building service"); 104 | let server_wasm = run_cargo_build_command(&config.service.package, "wasm32-wasip1", true)?; 105 | 106 | let client_wasm = if let Some(client_config) = &config.client { 107 | tracing::info!("Building client"); 108 | let client_wasm_path = run_cargo_build_command( 109 | &Some(client_config.package.to_string()), 110 | "wasm32-unknown-unknown", 111 | true, 112 | ) 113 | .expect("Error building client."); 114 | 115 | Bindgen::new() 116 | .input_path(client_wasm_path) 117 | .web(true)? 118 | .emit_start(false) 119 | .typescript(true) 120 | .generate("client-pkg")?; 121 | 122 | // TODO: run wasm-opt 123 | Some("client-pkg".to_string()) 124 | } else { 125 | None 126 | }; 127 | 128 | Ok(BuildResult { 129 | client_wasm, 130 | server_wasm: server_wasm.to_str().unwrap().to_string(), 131 | }) 132 | } 133 | -------------------------------------------------------------------------------- /stateroom-cli/src/cli_opts.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | 3 | #[derive(Parser)] 4 | pub struct Opts { 5 | #[clap(subcommand)] 6 | pub subcommand: SubCommand, 7 | } 8 | 9 | #[derive(Parser)] 10 | pub enum SubCommand { 11 | /// Run a dev server to host a given Stateroom module. 12 | Serve(ServeCommand), 13 | 14 | Build, 15 | Dev { 16 | #[clap(default_value = "8080")] 17 | port: u16, 18 | }, 19 | } 20 | 21 | #[derive(Parser)] 22 | pub struct LoginCommand { 23 | #[clap(short, long)] 24 | pub token: Option, 25 | 26 | #[clap(short, long)] 27 | pub clear: bool, 28 | } 29 | 30 | #[derive(Parser)] 31 | pub struct ServeCommand { 32 | /// The module (.wasm file) to serve. 33 | pub module: String, 34 | 35 | /// The port to serve on. 36 | #[clap(short, long, default_value = "8080")] 37 | pub port: u16, 38 | 39 | /// The time interval (in seconds) between WebSocket heartbeat pings. 40 | #[clap(short = 'i', long, default_value = "30")] 41 | pub heartbeat_interval: u64, 42 | 43 | /// The duration of time without hearing from a client before it is 44 | /// assumed to be disconnected. 45 | #[clap(short = 't', long, default_value = "120")] 46 | pub heartbeat_timeout: u64, 47 | } 48 | -------------------------------------------------------------------------------- /stateroom-cli/src/commands/build.rs: -------------------------------------------------------------------------------- 1 | use crate::build_util::{do_build, locate_config}; 2 | use anyhow::Context; 3 | use fs_extra::dir::CopyOptions; 4 | use std::{ 5 | fs::{copy, create_dir, remove_dir_all}, 6 | path::Path, 7 | }; 8 | 9 | const OUTPUT_DIR: &str = "dist"; 10 | const STATIC_DIR: &str = "static"; 11 | 12 | pub fn build() -> anyhow::Result<()> { 13 | let config = locate_config()?; // TODO: default to a configuration if file not found. 14 | 15 | let build_result = do_build(&config)?; 16 | 17 | if Path::new(OUTPUT_DIR).exists() { 18 | remove_dir_all(OUTPUT_DIR).context("Couldn't delete dist directory.")?; 19 | } 20 | create_dir(OUTPUT_DIR).context("Couldn't create dist directory.")?; 21 | 22 | copy( 23 | build_result.server_wasm, 24 | Path::new(OUTPUT_DIR).join("server.wasm"), 25 | ) 26 | .context("Couldn't copy server.wasm.")?; 27 | 28 | create_dir(Path::new(OUTPUT_DIR).join(STATIC_DIR)) 29 | .context("Couldn't create empty static directory.")?; 30 | 31 | if let Some(static_dir) = config.static_files { 32 | fs_extra::dir::copy( 33 | static_dir, 34 | Path::new(OUTPUT_DIR).join(STATIC_DIR), 35 | &CopyOptions { 36 | content_only: true, 37 | ..CopyOptions::default() 38 | }, 39 | ) 40 | .context("Couldn't copy static items.")?; 41 | } 42 | 43 | if let Some(client_wasm) = build_result.client_wasm { 44 | fs_extra::dir::copy( 45 | client_wasm, 46 | Path::new(OUTPUT_DIR).join(STATIC_DIR).join("client"), 47 | &CopyOptions { 48 | copy_inside: true, 49 | ..CopyOptions::default() 50 | }, 51 | ) 52 | .context("Couldn't copy client wasm.")?; 53 | } 54 | 55 | Ok(()) 56 | } 57 | -------------------------------------------------------------------------------- /stateroom-cli/src/commands/dev.rs: -------------------------------------------------------------------------------- 1 | use crate::build_util::{do_build, locate_config}; 2 | use stateroom_server::Server; 3 | use stateroom_wasm_host::WasmHostFactory; 4 | 5 | pub fn dev(port: u16) -> anyhow::Result<()> { 6 | let config = locate_config()?; // TODO: default to a configuration if file not found. 7 | 8 | let build_result = do_build(&config)?; 9 | let host_factory = WasmHostFactory::new(build_result.server_wasm)?; 10 | 11 | Server::default() 12 | .with_port(port) 13 | .with_static_path(config.static_files) 14 | .with_client_path(build_result.client_wasm) 15 | .serve(host_factory) 16 | .map_err(|e| e.into()) 17 | } 18 | -------------------------------------------------------------------------------- /stateroom-cli/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod build; 2 | pub mod dev; 3 | pub mod serve; 4 | -------------------------------------------------------------------------------- /stateroom-cli/src/commands/serve.rs: -------------------------------------------------------------------------------- 1 | use crate::cli_opts::ServeCommand; 2 | use stateroom_server::Server; 3 | use stateroom_wasm_host::WasmHostFactory; 4 | use std::{ffi::OsStr, path::Path, time::Duration}; 5 | 6 | pub fn serve(serve_opts: ServeCommand) -> anyhow::Result<()> { 7 | let ServeCommand { 8 | module, 9 | port, 10 | heartbeat_interval, 11 | heartbeat_timeout, 12 | } = serve_opts; 13 | 14 | let path = Path::new(&module); 15 | let ext = path 16 | .extension() 17 | .and_then(OsStr::to_str) 18 | .map(str::to_ascii_lowercase); 19 | 20 | let server_settings = Server { 21 | heartbeat_interval: Duration::from_secs(heartbeat_interval), 22 | heartbeat_timeout: Duration::from_secs(heartbeat_timeout), 23 | port, 24 | ..Server::default() 25 | }; 26 | 27 | if let Some("wasm" | "wat") = ext.as_deref() { 28 | let host_factory = WasmHostFactory::new(&module)?; 29 | server_settings.serve(host_factory).map_err(|e| e.into()) 30 | } else if path.is_file() { 31 | unimplemented!("Only .wasm and .wat files are supported."); 32 | } else if path.is_dir() { 33 | let server_module = path.join("server.wasm"); 34 | 35 | if !server_module.exists() { 36 | return Err(anyhow::anyhow!("Expected server.wasm")); 37 | } 38 | 39 | let static_dir = path.join("static"); 40 | 41 | let static_dir = if static_dir.exists() { 42 | Some(static_dir.to_str().unwrap().to_string()) 43 | } else { 44 | None 45 | }; 46 | 47 | let host_factory = WasmHostFactory::new(&server_module)?; 48 | 49 | server_settings 50 | .with_static_path(static_dir) 51 | .serve(host_factory) 52 | .map_err(|e| e.into()) 53 | } else { 54 | Err(anyhow::anyhow!("Expected a file or directory.")) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /stateroom-cli/src/config.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, Debug, Default)] 4 | pub struct GlobalConfig { 5 | pub token: Option, 6 | } 7 | 8 | /// Represents a `stateroom.toml` file, used to configure 9 | /// a Stateroom server. 10 | #[derive(Serialize, Deserialize, Debug, Default)] 11 | pub struct StateroomConfig { 12 | /// Directory to serve static files from. 13 | /// 14 | /// If this is provided, the server will attempt to serve HTTP requests 15 | /// relative to this directory, if they do not match other paths. 16 | pub static_files: Option, 17 | 18 | /// A unique ID for the service used for deployment. 19 | pub service_id: Option, 20 | 21 | /// Optional configuration for building a WebAssembly module for the 22 | /// client. 23 | /// 24 | /// This allows you to use `stateroom dev` to build both the server- and 25 | /// client-side code from the same workspace in one command. 26 | pub client: Option, 27 | 28 | /// Configuration for building the WebAssembly module to serve. 29 | #[serde(default)] 30 | pub service: ServiceConfig, 31 | } 32 | 33 | /// Configuration for generating a client-side WebAssembly module. 34 | #[derive(Serialize, Deserialize, Debug)] 35 | pub struct ClientConfig { 36 | /// The name of the package to build. 37 | /// 38 | /// Must be discoverable by cargo from the directory that `stateroom` 39 | /// is run from (i.e. `cargo build -p ` should succeed) 40 | pub package: String, 41 | pub optimization_level: Option, 42 | } 43 | 44 | /// Configuration for generating and serving a Stateroom service module. 45 | #[derive(Serialize, Deserialize, Debug, Default)] 46 | pub struct ServiceConfig { 47 | /// The name of the package to build. 48 | /// 49 | /// Must be discoverable by cargo from the directory that `stateroom` 50 | /// is run from (i.e. `cargo build -p ` should succeed) 51 | /// 52 | /// If this is empty, builds the package we are in (i.e. the package that 53 | /// `cargo build` builds.) 54 | pub package: Option, 55 | } 56 | -------------------------------------------------------------------------------- /stateroom-cli/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod cli_opts; 2 | mod commands; 3 | mod config; 4 | 5 | pub use commands::build::build; 6 | pub use commands::dev::dev; 7 | pub use commands::serve::serve; 8 | mod build_util; 9 | -------------------------------------------------------------------------------- /stateroom-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stateroom-server" 3 | version = "0.4.4" 4 | edition = "2021" 5 | readme = "../README.md" 6 | repository = "https://github.com/drifting-in-space/stateroom" 7 | license = "MIT OR Apache-2.0" 8 | keywords = ["websocket"] 9 | description = "Server for Stateroom services over WebSockets" 10 | 11 | [dependencies] 12 | axum = { version = "0.7.5", features = ["ws"] } 13 | dashmap = "6" 14 | futures-util = "0.3.30" 15 | stateroom = {path="../stateroom", version="0.4.1"} 16 | tokio = { version = "1.37.0", features = ["rt-multi-thread"] } 17 | tower-http = { version="0.5.2", features=["fs"] } 18 | tracing = "0.1.40" 19 | -------------------------------------------------------------------------------- /stateroom-server/src/lib.rs: -------------------------------------------------------------------------------- 1 | use crate::server::Event; 2 | use axum::{ 3 | extract::{ws::WebSocket, State, WebSocketUpgrade}, 4 | routing::get, 5 | Router, 6 | }; 7 | use server::ServerState; 8 | use stateroom::StateroomServiceFactory; 9 | use std::{ 10 | net::{IpAddr, SocketAddr}, 11 | sync::Arc, 12 | time::Duration, 13 | }; 14 | use tokio::{net::TcpListener, select}; 15 | use tower_http::services::ServeDir; 16 | 17 | mod server; 18 | 19 | const DEFAULT_IP: &str = "0.0.0.0"; 20 | 21 | #[derive(Debug)] 22 | pub struct Server { 23 | /// The duration of time between server-initiated WebSocket heartbeats. 24 | /// 25 | /// Defaults to 30 seconds. 26 | pub heartbeat_interval: Duration, 27 | 28 | /// The minimum amount of time between client heartbeats before a connection is dropped. 29 | /// 30 | /// Defaults to 5 minutes. 31 | pub heartbeat_timeout: Duration, 32 | 33 | /// The port to run the server on. Defaults to 8080. 34 | pub port: u16, 35 | 36 | /// The IP to listen on. Defaults to 0.0.0.0. 37 | pub ip: String, 38 | 39 | /// A local filesystem path to serve static files from, or None (default). 40 | pub static_path: Option, 41 | 42 | /// A local filesystem path to serve from /client, or None (default). 43 | pub client_path: Option, 44 | } 45 | 46 | impl Default for Server { 47 | fn default() -> Self { 48 | Server { 49 | heartbeat_interval: Duration::from_secs(30), 50 | heartbeat_timeout: Duration::from_secs(300), 51 | port: 8080, 52 | ip: DEFAULT_IP.to_string(), 53 | static_path: None, 54 | client_path: None, 55 | } 56 | } 57 | } 58 | 59 | impl Server { 60 | #[must_use] 61 | pub fn new() -> Self { 62 | Server::default() 63 | } 64 | 65 | #[must_use] 66 | pub fn with_static_path(mut self, static_path: Option) -> Self { 67 | self.static_path = static_path; 68 | self 69 | } 70 | 71 | #[must_use] 72 | pub fn with_client_path(mut self, client_path: Option) -> Self { 73 | self.client_path = client_path; 74 | self 75 | } 76 | 77 | #[must_use] 78 | pub fn with_heartbeat_interval(mut self, duration_seconds: u64) -> Self { 79 | self.heartbeat_interval = Duration::from_secs(duration_seconds); 80 | self 81 | } 82 | 83 | #[must_use] 84 | pub fn with_heartbeat_timeout(mut self, duration_seconds: u64) -> Self { 85 | self.heartbeat_timeout = Duration::from_secs(duration_seconds); 86 | self 87 | } 88 | 89 | #[must_use] 90 | pub fn with_port(mut self, port: u16) -> Self { 91 | self.port = port; 92 | self 93 | } 94 | 95 | #[must_use] 96 | pub fn with_ip(mut self, ip: String) -> Self { 97 | self.ip = ip; 98 | self 99 | } 100 | 101 | /// Start a server given a [StateroomService]. 102 | /// 103 | /// This function blocks until the server is terminated. While it is running, the following 104 | /// endpoints are available: 105 | /// - `/` (GET): return HTTP 200 if the server is running (useful as a baseline status check) 106 | /// - `/ws` (GET): initiate a WebSocket connection to the stateroom service. 107 | pub async fn serve_async(self, factory: impl StateroomServiceFactory) -> std::io::Result<()> { 108 | let server_state = Arc::new(ServerState::new(factory)); 109 | 110 | let mut app = Router::new() 111 | .route("/ws", get(serve_websocket)) 112 | .with_state(server_state); 113 | 114 | if let Some(static_path) = self.static_path { 115 | app = app.nest_service("/", ServeDir::new(static_path)); 116 | } 117 | 118 | if let Some(client_path) = self.client_path { 119 | app = app.nest_service("/client", ServeDir::new(client_path)); 120 | } 121 | 122 | let ip = self.ip.parse::().unwrap(); 123 | let addr = SocketAddr::new(ip, self.port); 124 | let listener = TcpListener::bind(&addr).await?; 125 | axum::serve(listener, app).await?; 126 | 127 | Ok(()) 128 | } 129 | 130 | /// Start a server given a [StateroomService]. 131 | /// 132 | /// This function blocks until the server is terminated. While it is running, the following 133 | /// endpoints are available: 134 | /// - `/` (GET): return HTTP 200 if the server is running (useful as a baseline status check) 135 | /// - `/ws` (GET): initiate a WebSocket connection to the stateroom service. 136 | pub fn serve(self, factory: impl StateroomServiceFactory) -> std::io::Result<()> { 137 | tokio::runtime::Builder::new_multi_thread() 138 | .enable_all() 139 | .build() 140 | .unwrap() 141 | .block_on(async { self.serve_async(factory).await }) 142 | } 143 | } 144 | 145 | pub async fn serve_websocket( 146 | ws: WebSocketUpgrade, 147 | State(state): State>, 148 | ) -> axum::response::Response { 149 | ws.on_upgrade(move |socket| handle_socket(socket, state)) 150 | } 151 | 152 | async fn handle_socket(mut socket: WebSocket, state: Arc) { 153 | let (send, mut recv, client_id) = state.connect(); 154 | 155 | loop { 156 | select! { 157 | msg = recv.recv() => { 158 | match msg { 159 | Some(msg) => socket.send(msg).await.unwrap(), 160 | None => break, 161 | } 162 | }, 163 | msg = socket.recv() => { 164 | match msg { 165 | Some(Ok(msg)) => send.send(Event::Message { client: client_id, message: msg }).await.unwrap(), 166 | Some(Err(err)) => { 167 | tracing::warn!(?err, "Error receiving message from client."); 168 | }, 169 | None => break, 170 | } 171 | } 172 | } 173 | } 174 | 175 | state.remove(&client_id); 176 | } 177 | -------------------------------------------------------------------------------- /stateroom-server/src/server.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::ws::Message; 2 | use dashmap::DashMap; 3 | use stateroom::{ 4 | ClientId, MessagePayload, MessageRecipient, StateroomContext, StateroomService, 5 | StateroomServiceFactory, 6 | }; 7 | use std::{ 8 | sync::{atomic::AtomicU32, Arc, Mutex}, 9 | time::Duration, 10 | }; 11 | use tokio::{ 12 | sync::mpsc::{Receiver, Sender}, 13 | task::JoinHandle, 14 | }; 15 | 16 | /// A [StateroomContext] implementation for [StateroomService]s hosted in the 17 | /// context of a [ServiceActor]. 18 | #[derive(Clone)] 19 | pub struct ServerStateroomContext { 20 | senders: Arc>>, 21 | event_sender: Arc>, 22 | timer_handle: Arc>>>, 23 | } 24 | 25 | impl ServerStateroomContext { 26 | pub fn try_send(&self, recipient: MessageRecipient, message: Message) { 27 | match recipient { 28 | MessageRecipient::Broadcast => { 29 | for sender in self.senders.iter() { 30 | sender.value().try_send(message.clone()).unwrap(); 31 | } 32 | } 33 | MessageRecipient::EveryoneExcept(skip_client_id) => { 34 | for sender in self.senders.iter() { 35 | if sender.key() != &skip_client_id { 36 | sender.try_send(message.clone()).unwrap(); 37 | } 38 | } 39 | } 40 | MessageRecipient::Client(client_id) => { 41 | if let Some(sender) = self.senders.get(&client_id) { 42 | sender.try_send(message).unwrap(); 43 | } else { 44 | tracing::error!(?client_id, "No sender for client."); 45 | } 46 | } 47 | } 48 | } 49 | } 50 | 51 | impl StateroomContext for ServerStateroomContext { 52 | fn send_message( 53 | &self, 54 | recipient: impl Into, 55 | message: impl Into, 56 | ) { 57 | let message: MessagePayload = message.into(); 58 | let message: Message = match message { 59 | MessagePayload::Text(s) => Message::Text(s), 60 | MessagePayload::Bytes(b) => Message::Binary(b), 61 | }; 62 | self.try_send(recipient.into(), message); 63 | } 64 | 65 | fn set_timer(&self, ms_delay: u32) { 66 | let sender = self.event_sender.clone(); 67 | let handle = tokio::spawn(async move { 68 | tokio::time::sleep(Duration::from_millis(ms_delay as u64)).await; 69 | sender.send(Event::Timer).await.unwrap(); 70 | }); 71 | 72 | let mut c = self 73 | .timer_handle 74 | .lock() 75 | .expect("timer handle lock poisoned"); 76 | if let Some(c) = c.take() { 77 | c.abort(); 78 | } 79 | *c = Some(handle); 80 | } 81 | } 82 | 83 | #[derive(Debug)] 84 | pub struct ServerState { 85 | pub handle: JoinHandle<()>, 86 | pub inbound_sender: Sender, 87 | pub senders: Arc>>, 88 | pub next_client_id: AtomicU32, 89 | } 90 | 91 | #[derive(Debug)] 92 | pub enum Event { 93 | Message { client: ClientId, message: Message }, 94 | Join { client: ClientId }, 95 | Leave { client: ClientId }, 96 | Timer, 97 | } 98 | 99 | impl ServerState { 100 | pub fn new(factory: impl StateroomServiceFactory) -> Self { 101 | let (tx, mut rx) = tokio::sync::mpsc::channel::(100); 102 | 103 | let senders = Arc::new(DashMap::new()); 104 | 105 | let senders_ = senders.clone(); 106 | let tx_ = tx.clone(); 107 | let handle = tokio::spawn(async move { 108 | let context = Arc::new(ServerStateroomContext { 109 | senders: senders_.clone(), 110 | event_sender: Arc::new(tx_), 111 | timer_handle: Arc::new(Mutex::new(None)), 112 | }); 113 | 114 | let mut service = factory.build("", context.clone()).unwrap(); 115 | service.init(context.as_ref()); 116 | 117 | loop { 118 | let msg = rx.recv().await; 119 | match msg { 120 | Some(Event::Message { client, message }) => match message { 121 | Message::Text(msg) => { 122 | service.message(client, MessagePayload::Text(msg), context.as_ref()) 123 | } 124 | Message::Binary(msg) => { 125 | service.message(client, MessagePayload::Bytes(msg), context.as_ref()) 126 | } 127 | Message::Close(_) => {} 128 | msg => tracing::warn!("Ignoring unhandled message: {:?}", msg), 129 | }, 130 | Some(Event::Join { client }) => service.connect(client, context.as_ref()), 131 | Some(Event::Leave { client }) => service.disconnect(client, context.as_ref()), 132 | Some(Event::Timer) => { 133 | service.timer(context.as_ref()); 134 | } 135 | None => break, 136 | } 137 | } 138 | }); 139 | 140 | Self { 141 | handle, 142 | inbound_sender: tx, 143 | senders, 144 | next_client_id: AtomicU32::new(1), 145 | } 146 | } 147 | 148 | pub fn remove(&self, client: &ClientId) { 149 | self.inbound_sender 150 | .try_send(Event::Leave { client: *client }) 151 | .unwrap(); 152 | self.senders.remove(client); 153 | } 154 | 155 | pub fn connect(&self) -> (Sender, Receiver, ClientId) { 156 | let client_id = self.next_client_id(); 157 | let (tx, rx) = tokio::sync::mpsc::channel::(100); 158 | 159 | self.senders.insert(client_id, tx); 160 | self.inbound_sender 161 | .try_send(Event::Join { client: client_id }) 162 | .unwrap(); 163 | (self.inbound_sender.clone(), rx, client_id) 164 | } 165 | 166 | fn next_client_id(&self) -> ClientId { 167 | let r = self 168 | .next_client_id 169 | .fetch_add(1, std::sync::atomic::Ordering::Relaxed); 170 | ClientId(r) 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /stateroom-wasm-host/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stateroom-wasm-host" 3 | version = "0.4.4" 4 | edition = "2018" 5 | readme = "README.md" 6 | repository = "https://github.com/drifting-in-space/stateroom" 7 | license = "MIT OR Apache-2.0" 8 | keywords = ["websocket", "webassembly", "stateroom"] 9 | description = "A Stateroom service implementation that takes a WebAssembly module and delegates behavior to it." 10 | 11 | [dependencies] 12 | anyhow = "1.0.45" 13 | byteorder = "1.4.3" 14 | stateroom = {path="../stateroom", version="0.4.0", features=["serde"]} 15 | wasmtime = "24.0.0" 16 | tracing = "0.1.28" 17 | wasi-common = "24.0.0" 18 | bincode = "1.3.3" 19 | -------------------------------------------------------------------------------- /stateroom-wasm-host/README.md: -------------------------------------------------------------------------------- 1 | # stateroom-wasm-host 2 | 3 | This crate loads a Stateroom WebAssembly module and wraps it in an interface 4 | that implements StateroomService, so that it can be used interchangably with 5 | native StateroomService implementations. If you only want to serve WebAssembly 6 | modules, you can use the `stateroom` command-line application (which uses this 7 | crate) instead of using this crate directly. 8 | 9 | ## WebAssembly interface 10 | 11 | The host expects that the WebAssembly module provided to it will conform to 12 | a particular interface (that is, has particular named imports and exports). 13 | Eventually, WebAssembly extensions like Interface Types may allow us to 14 | formalize this interface. 15 | 16 | Code generated by `stateroom-wasm-macro` automatically conforms to the interface, 17 | but if you are implementing in a non-Rust language, it's up to you to ensure that 18 | imports and exports are available and correctly typed, or else a runtime error 19 | will occur when the module is loaded. 20 | 21 | ### Exports 22 | 23 | The module is expected to export these functions: 24 | 25 | - `fn jam_malloc(size: u32) -> u32`: Allocate `size` bytes of memory inside the WebAssembly module and return a pointer. 26 | - `fn jam_free(loc: *mut u8, size: u32)`: Free `size` bytes of memory starting at `loc`. 27 | - `fn initialize(room_id_ptr: *const u8, room_id_len: u32)`: Initialize the object with the provided room ID (passed as a pointer, length pair). 28 | - `fn connect(client_id: u32)`: Called immediately after the given user has connected. 29 | - `fn disconnect(client_id: u32)`: Called immediately after the given user has disconnected. 30 | - `fn timer()`: Called if the instance set a timer which has triggered (see `set_timer()` under imports). 31 | - `fn message(client_id: u32, ptr: *const u8, len: u32)`: Called when the instance receives a text message from a client. The message is passed as a (pointer, length) pair. 32 | - `fn binary(client_id: u32, ptr: *const u8, len: u32)`: Called when the instance receives a binary message from a client. The message is passed as a (pointer, length) pair. 33 | 34 | ### Imports 35 | 36 | The module may import any of these functions from the environment: 37 | 38 | - `fn send_message(client_id: u32, message: *const u8, len: u32)`: Send the text message, provided as a (pointer, length) pair. If `client_id == 0`, the mesage will 39 | be broadcast to all connected users. 40 | - `fn send_binary(client_id: u32, message: *const u8, len: u32)`: Send the binary message, provided as a (pointer, length) pair. If `client_id == 0`, the mesage will 41 | be broadcast to all connected users. 42 | - `fn set_timer(ms_delay: u32)`: Asks the host runtime to call `timer()` in a given 43 | number of milliseconds. Replaces any previous timer request. If `ms_delay` is 0, 44 | the previous timer will be cancelled but no new timer will be set. 45 | -------------------------------------------------------------------------------- /stateroom-wasm-host/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This module provides a [stateroom::StateroomService] implementation that is backed by a 2 | //! WebAssembly module. It is the counterpart to `stateroom-wasm`, which is used to 3 | //! implement a compatible guest module. 4 | 5 | use std::{ 6 | error::Error, 7 | fmt::{Debug, Display}, 8 | }; 9 | pub use wasm_host::WasmHost; 10 | pub use wasm_host_factory::WasmHostFactory; 11 | 12 | mod wasm_host; 13 | mod wasm_host_factory; 14 | 15 | /// An error encountered while running WebAssembly. 16 | #[derive(Debug)] 17 | pub enum WasmRuntimeError { 18 | CouldNotImportMemory, 19 | CouldNotImportGlobal, 20 | InvalidApiVersion, 21 | InvalidProtocolVersion, 22 | } 23 | 24 | impl Display for WasmRuntimeError { 25 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 26 | Debug::fmt(&self, f) 27 | } 28 | } 29 | 30 | impl Error for WasmRuntimeError { 31 | fn description(&self) -> &str { 32 | match self { 33 | Self::CouldNotImportMemory => "Could not import memory from wasm instance.", 34 | Self::CouldNotImportGlobal => "Could not read global variable from wasm instance.", 35 | Self::InvalidApiVersion => { 36 | "WebAssembly module has an incompatible Stateroom API version." 37 | } 38 | Self::InvalidProtocolVersion => { 39 | "WebAssembly module has an incompatible Stateroom protocol version." 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /stateroom-wasm-host/src/wasm_host.rs: -------------------------------------------------------------------------------- 1 | use crate::WasmRuntimeError; 2 | use anyhow::{Context, Result}; 3 | use byteorder::{LittleEndian, ReadBytesExt}; 4 | use stateroom::{ 5 | ClientId, MessageFromProcess, MessagePayload, MessageToProcess, StateroomContext, 6 | StateroomService, 7 | }; 8 | use std::{borrow::BorrowMut, sync::Arc}; 9 | use wasi_common::{sync::WasiCtxBuilder, WasiCtx}; 10 | use wasmtime::{Caller, Engine, Extern, Instance, Linker, Memory, Module, Store, TypedFunc, Val}; 11 | 12 | const ENV: &str = "env"; 13 | const EXT_MEMORY: &str = "memory"; 14 | const EXT_FN_SEND: &str = "stateroom_send"; 15 | const EXT_FN_RECV: &str = "stateroom_recv"; 16 | const EXT_FN_MALLOC: &str = "stateroom_malloc"; 17 | const EXT_FN_FREE: &str = "stateroom_free"; 18 | const EXT_STATEROOM_VERSION: &str = "STATEROOM_API_VERSION"; 19 | const EXT_STATEROOM_PROTOCOL: &str = "STATEROOM_API_PROTOCOL"; 20 | 21 | const EXPECTED_API_VERSION: i32 = 1; 22 | const EXPECTED_PROTOCOL_VERSION: i32 = 0; 23 | 24 | /// Hosts a [stateroom::StateroomService] implemented by a WebAssembly module. 25 | pub struct WasmHost { 26 | store: Store, 27 | memory: Memory, 28 | 29 | fn_malloc: TypedFunc, 30 | fn_free: TypedFunc<(u32, u32), ()>, 31 | fn_recv: TypedFunc<(u32, u32), ()>, 32 | } 33 | 34 | impl WasmHost { 35 | fn put_data(&mut self, data: &[u8]) -> Result<(u32, u32)> { 36 | #[allow(clippy::cast_possible_truncation)] 37 | let len = data.len() as u32; 38 | let pt = self.fn_malloc.call(&mut self.store, len)?; 39 | 40 | self.memory.write(&mut self.store, pt as usize, data)?; 41 | 42 | Ok((pt, len)) 43 | } 44 | 45 | fn try_recv(&mut self, message: MessageToProcess) -> Result<()> { 46 | let payload = bincode::serialize(&message).unwrap(); 47 | let (pt, len) = self.put_data(&payload)?; 48 | 49 | self.fn_recv.call(&mut self.store, (pt, len))?; 50 | self.fn_free.call(&mut self.store, (pt, len))?; 51 | 52 | Ok(()) 53 | } 54 | } 55 | 56 | impl StateroomService for WasmHost { 57 | fn init(&mut self, _: &impl StateroomContext) { 58 | let message = MessageToProcess::Init; 59 | self.try_recv(message).unwrap(); 60 | } 61 | 62 | fn message(&mut self, sender: ClientId, message: MessagePayload, _: &impl StateroomContext) { 63 | let message = MessageToProcess::Message { sender, message }; 64 | self.try_recv(message).unwrap(); 65 | } 66 | 67 | fn connect(&mut self, client: ClientId, _: &impl StateroomContext) { 68 | let message = MessageToProcess::Connect { client }; 69 | self.try_recv(message).unwrap(); 70 | } 71 | 72 | fn disconnect(&mut self, client: ClientId, _: &impl StateroomContext) { 73 | let message = MessageToProcess::Disconnect { client }; 74 | self.try_recv(message).unwrap(); 75 | } 76 | 77 | fn timer(&mut self, _: &impl StateroomContext) { 78 | let message = MessageToProcess::Timer; 79 | self.try_recv(message).unwrap(); 80 | } 81 | } 82 | 83 | #[inline] 84 | fn get_memory(caller: &mut Caller<'_, T>) -> Memory { 85 | match caller.get_export(EXT_MEMORY) { 86 | Some(Extern::Memory(mem)) => mem, 87 | _ => panic!(), 88 | } 89 | } 90 | 91 | #[inline] 92 | fn get_u8_vec<'a, T>( 93 | caller: &'a Caller<'_, T>, 94 | memory: &'a Memory, 95 | start: u32, 96 | len: u32, 97 | ) -> &'a [u8] { 98 | let data = memory 99 | .data(caller) 100 | .get(start as usize..(start + len) as usize); 101 | match data { 102 | Some(data) => data, 103 | None => panic!(), 104 | } 105 | } 106 | 107 | pub fn get_global( 108 | store: &mut Store, 109 | memory: &mut Memory, 110 | instance: &Instance, 111 | name: &str, 112 | ) -> Result { 113 | #[allow(clippy::cast_sign_loss)] 114 | let i: u32 = { 115 | let mem_location = instance 116 | .get_global(store.borrow_mut(), name) 117 | .ok_or(WasmRuntimeError::CouldNotImportGlobal)?; 118 | 119 | match mem_location.get(store.borrow_mut()) { 120 | Val::I32(i) => Ok(i), 121 | _ => Err(WasmRuntimeError::CouldNotImportGlobal), 122 | }? as u32 123 | }; 124 | 125 | #[allow(clippy::cast_possible_truncation)] 126 | let mut value = memory 127 | .data(store) 128 | .get(i as usize..(i as usize + std::mem::size_of::())) 129 | .ok_or(WasmRuntimeError::CouldNotImportGlobal)?; 130 | let result = value.read_i32::()?; 131 | Ok(result) 132 | } 133 | 134 | impl WasmHost { 135 | pub fn new( 136 | room_id: &str, 137 | module: &Module, 138 | engine: &Engine, 139 | context: Arc, 140 | ) -> Result { 141 | let wasi = WasiCtxBuilder::new().inherit_stdio().build(); 142 | 143 | let mut store = Store::new(engine, wasi); 144 | let mut linker = Linker::new(engine); 145 | wasi_common::sync::add_to_linker(&mut linker, |s| s)?; 146 | 147 | { 148 | #[allow(clippy::redundant_clone)] 149 | let context = context.clone(); 150 | linker.func_wrap( 151 | ENV, 152 | EXT_FN_SEND, 153 | move |mut caller: Caller<'_, WasiCtx>, start: u32, len: u32| { 154 | let memory = get_memory(&mut caller); 155 | let message = get_u8_vec(&caller, &memory, start, len); 156 | let message: MessageFromProcess = bincode::deserialize(message).unwrap(); 157 | 158 | match message { 159 | MessageFromProcess::Message { recipient, message } => { 160 | context.send_message(recipient, message); 161 | } 162 | MessageFromProcess::SetTimer { ms_delay } => { 163 | context.set_timer(ms_delay); 164 | } 165 | }; 166 | 167 | Ok(()) 168 | }, 169 | )?; 170 | } 171 | 172 | let instance = linker.instantiate(&mut store, module)?; 173 | 174 | let fn_malloc = instance.get_typed_func::(&mut store, EXT_FN_MALLOC)?; 175 | 176 | let fn_free = instance.get_typed_func::<(u32, u32), ()>(&mut store, EXT_FN_FREE)?; 177 | 178 | let fn_recv = instance.get_typed_func::<(u32, u32), ()>(&mut store, EXT_FN_RECV)?; 179 | 180 | let mut memory = instance 181 | .get_memory(&mut store, EXT_MEMORY) 182 | .ok_or(WasmRuntimeError::CouldNotImportMemory)?; 183 | 184 | { 185 | let room_id = room_id.as_bytes(); 186 | #[allow(clippy::cast_possible_truncation)] 187 | let len = room_id.len() as u32; 188 | let pt = fn_malloc.call(&mut store, len)?; 189 | 190 | memory.write(&mut store, pt as usize, room_id)?; 191 | 192 | fn_free.call(&mut store, (pt, len))?; 193 | } 194 | 195 | if get_global(&mut store, &mut memory, &instance, EXT_STATEROOM_VERSION) 196 | .context("Stateroom version")? 197 | != EXPECTED_API_VERSION 198 | { 199 | return Err(WasmRuntimeError::InvalidApiVersion.into()); 200 | } 201 | 202 | if get_global(&mut store, &mut memory, &instance, EXT_STATEROOM_PROTOCOL) 203 | .context("Stateroom protocol")? 204 | != EXPECTED_PROTOCOL_VERSION 205 | { 206 | return Err(WasmRuntimeError::InvalidProtocolVersion.into()); 207 | } 208 | 209 | Ok(WasmHost { 210 | store, 211 | memory, 212 | fn_malloc, 213 | fn_free, 214 | fn_recv, 215 | }) 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /stateroom-wasm-host/src/wasm_host_factory.rs: -------------------------------------------------------------------------------- 1 | use crate::wasm_host::WasmHost; 2 | use anyhow::Result; 3 | use stateroom::{StateroomContext, StateroomServiceFactory}; 4 | use std::{path::Path, sync::Arc}; 5 | use wasmtime::{Engine, Module}; 6 | 7 | /// Loads and caches a WebAssembly module such that a [WasmHost] instance can be 8 | /// created from it. 9 | /// 10 | /// This struct is cheaply cloneable, so it can be used to create multiple instances 11 | /// of the same module. 12 | #[derive(Clone)] 13 | pub struct WasmHostFactory { 14 | engine: Arc, 15 | module: Arc, 16 | } 17 | 18 | impl StateroomServiceFactory for WasmHostFactory { 19 | type Service = WasmHost; 20 | type Error = anyhow::Error; 21 | 22 | fn build( 23 | &self, 24 | room_id: &str, 25 | context: Arc, 26 | ) -> Result { 27 | WasmHost::new(room_id, self.module.as_ref(), self.engine.as_ref(), context) 28 | } 29 | } 30 | 31 | impl WasmHostFactory { 32 | pub fn new

(wasm_file: P) -> Result 33 | where 34 | P: AsRef, 35 | { 36 | let engine = Engine::default(); 37 | tracing::info!(wasm_file=?wasm_file.as_ref(), "Loading WebAssembly module"); 38 | let module = Module::from_file(&engine, wasm_file)?; 39 | tracing::info!("WebAssembly module loaded"); 40 | 41 | Ok(WasmHostFactory { 42 | engine: Arc::new(engine), 43 | module: Arc::new(module), 44 | }) 45 | } 46 | 47 | #[must_use] 48 | pub fn new_with_shared_module(engine: Arc, module: Arc) -> Self { 49 | WasmHostFactory { engine, module } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /stateroom-wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stateroom-wasm" 3 | version = "0.4.4" 4 | edition = "2018" 5 | readme = "README.md" 6 | repository = "https://github.com/drifting-in-space/stateroom" 7 | license = "MIT OR Apache-2.0" 8 | keywords = ["websocket", "webassembly", "stateroom"] 9 | description = "A macro for building a Stateroom service as a WebAssembly module." 10 | 11 | [dependencies] 12 | stateroom-wasm-macro = {path="./stateroom-wasm-macro", version="0.4.0"} 13 | stateroom = {path="../stateroom", version="0.4.0", features=["serde"]} 14 | bincode = "1.3.3" 15 | -------------------------------------------------------------------------------- /stateroom-wasm/README.md: -------------------------------------------------------------------------------- 1 | # `stateroom-wasm` 2 | 3 | `stateroom-wasm` is a companion crate to `stateroom` that helps you package and 4 | export services as WebAssembly modules. 5 | 6 | ## `#[stateroom_wasm]` macro 7 | 8 | WebAssembly modules must import and export certain named functions in order for the 9 | Stateroom server to understand them. `stateroom_wasm` provides the `#[stateroom_wasm]` module, 10 | which should be applied to an item (`struct`, `enum`, or `type` alias) that implements 11 | `SimpleStateroomService`. 12 | 13 | A Stateroom-compatible WebAssembly module must contain exactly one service. If you 14 | need to generate multiple services for your application, currently the best approach 15 | is to make a crate for each service. 16 | 17 | It's possible to generate bindings for a service that belongs to another module using 18 | a `type` alias: 19 | 20 | ```rust 21 | use stateroom_wasm::stateroom_wasm; 22 | use some_module::SomeService; 23 | 24 | #[stateroom_wasm] 25 | type Service = SomeService; 26 | ``` 27 | 28 | ## Execution model 29 | 30 | Upon initialization, the generated model creates an instance of your `SimpleStateroomService` 31 | by calling its `new(room_id, context)` constructor. The context object that is passed in the 32 | constructor and subsequent function calls is a global static object that binds to functions 33 | imported from the host environment (like `send_message`). 34 | 35 | ## Compiling 36 | 37 | If you are using the Stateroom command-line interface, `stateroom dev` will build the 38 | current crate using the `wasm32-wasip1` target, and then load and serve the generated 39 | WebAssembly module. 40 | 41 | If you would like to build it manually, make sure you have the `wasm32-wasip1` target installed 42 | and pass it as a target to `cargo build`: 43 | 44 | ```bash 45 | $ rustup target add wasm32-wasip1 46 | $ cargo build --release --target=wasm32-wasip1 47 | ``` 48 | 49 | ## Embedding 50 | 51 | This crate has a counterpart, `stateroom-wasm-host`, which can take a module generated with 52 | this crate and expose it through a StateroomService interface. 53 | -------------------------------------------------------------------------------- /stateroom-wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | use stateroom::MessageFromProcess; 2 | pub use stateroom::{ClientId, MessageRecipient, StateroomContext, StateroomService}; 3 | pub use stateroom::{MessagePayload, MessageToProcess}; 4 | pub use stateroom_wasm_macro::stateroom_wasm; 5 | 6 | type Callback = unsafe extern "C" fn(*const u8, u32); 7 | 8 | pub struct WrappedStateroomService { 9 | state: S, 10 | context: WasmStateroomContext, 11 | } 12 | 13 | impl WrappedStateroomService { 14 | pub fn new(state: S, callback: Callback) -> Self { 15 | Self { 16 | state, 17 | context: WasmStateroomContext { callback }, 18 | } 19 | } 20 | 21 | pub fn recv(&mut self, message_ptr: *const u8, message_len: u32) { 22 | let message = unsafe { std::slice::from_raw_parts(message_ptr, message_len as usize) }; 23 | let message: MessageToProcess = bincode::deserialize(message).unwrap(); 24 | 25 | match message { 26 | MessageToProcess::Init => { 27 | self.state.init(&self.context); 28 | } 29 | MessageToProcess::Connect { client } => { 30 | self.state.connect(client, &self.context); 31 | } 32 | MessageToProcess::Disconnect { client } => { 33 | self.state.disconnect(client, &self.context); 34 | } 35 | MessageToProcess::Message { sender, message } => { 36 | self.state.message(sender, message, &self.context); 37 | } 38 | MessageToProcess::Timer => { 39 | self.state.timer(&self.context); 40 | } 41 | } 42 | } 43 | } 44 | 45 | #[derive(Clone)] 46 | struct WasmStateroomContext { 47 | callback: Callback, 48 | } 49 | 50 | impl WasmStateroomContext { 51 | pub fn send(&self, message: &MessageFromProcess) { 52 | let message = bincode::serialize(message).unwrap(); 53 | unsafe { 54 | (self.callback)(message.as_ptr(), message.len() as u32); 55 | } 56 | } 57 | } 58 | 59 | impl StateroomContext for WasmStateroomContext { 60 | fn send_message( 61 | &self, 62 | recipient: impl Into, 63 | message: impl Into, 64 | ) { 65 | let message: MessagePayload = message.into(); 66 | let recipient: MessageRecipient = recipient.into(); 67 | 68 | self.send(&MessageFromProcess::Message { recipient, message }); 69 | } 70 | 71 | fn set_timer(&self, ms_delay: u32) { 72 | self.send(&MessageFromProcess::SetTimer { ms_delay }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /stateroom-wasm/stateroom-wasm-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stateroom-wasm-macro" 3 | version = "0.4.0" 4 | authors = ["Paul Butler "] 5 | edition = "2018" 6 | readme = "README.md" 7 | repository = "https://github.com/drifting-in-space/stateroom" 8 | license = "MIT OR Apache-2.0" 9 | keywords = ["websocket", "webassembly", "stateroom"] 10 | description = "A macro for building a Stateroom service as a WebAssembly module." 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies] 16 | proc-macro2 = "1.0.36" 17 | quote = "1.0.14" 18 | syn = {version="2.0.15", features=["full"]} 19 | -------------------------------------------------------------------------------- /stateroom-wasm/stateroom-wasm-macro/README.md: -------------------------------------------------------------------------------- 1 | # stateroom-wasm-macro 2 | 3 | This crate contains a helper macro for generating WebAssembly from a [Stateroom](https://github.com/drifting-in-space/stateroom) 4 | service. You should use the `stateroom-wasm` crate instead of depending on this one. 5 | -------------------------------------------------------------------------------- /stateroom-wasm/stateroom-wasm-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | use proc_macro::TokenStream; 3 | use proc_macro2::Ident; 4 | use quote::quote; 5 | use syn::{ItemEnum, ItemStruct, ItemType}; 6 | 7 | fn get_name(item: &proc_macro2::TokenStream) -> Option { 8 | let ident = if let Ok(ItemStruct { ident, .. }) = syn::parse2(item.clone()) { 9 | ident 10 | } else if let Ok(ItemEnum { ident, .. }) = syn::parse2(item.clone()) { 11 | ident 12 | } else if let Ok(ItemType { ident, .. }) = syn::parse2(item.clone()) { 13 | ident 14 | } else { 15 | return None; 16 | }; 17 | 18 | Some(ident) 19 | } 20 | 21 | #[allow(clippy::too_many_lines)] 22 | fn stateroom_wasm_impl(item: &proc_macro2::TokenStream) -> proc_macro2::TokenStream { 23 | let name = 24 | get_name(item).expect("Can only use #[stateroom_wasm] on a struct, enum, or type alias."); 25 | 26 | quote! { 27 | #item 28 | 29 | mod _stateroom_wasm_macro_autogenerated { 30 | extern crate alloc; 31 | 32 | use super::#name; 33 | 34 | // Functions implemented by the host. 35 | mod ffi { 36 | extern "C" { 37 | pub fn stateroom_send(message_ptr: *const u8, message_len: u32); 38 | } 39 | } 40 | 41 | // Instance-global stateroom service. 42 | static mut SERVER_STATE: Option> = None; 43 | 44 | #[no_mangle] 45 | pub static STATEROOM_API_VERSION: i32 = 1; 46 | 47 | #[no_mangle] 48 | pub static STATEROOM_API_PROTOCOL: i32 = 0; 49 | 50 | #[no_mangle] 51 | extern "C" fn stateroom_recv(message_ptr: *const u8, message_len: u32) { 52 | let state = unsafe { 53 | match SERVER_STATE.as_mut() { 54 | Some(s) => s, 55 | None => { 56 | let s = stateroom_wasm::WrappedStateroomService::new(#name::default(), ffi::stateroom_send); 57 | SERVER_STATE.replace(s); 58 | SERVER_STATE.as_mut().unwrap() 59 | } 60 | } 61 | }; 62 | state.recv(message_ptr, message_len); 63 | } 64 | 65 | #[no_mangle] 66 | pub unsafe extern "C" fn stateroom_malloc(size: u32) -> *mut u8 { 67 | if size == 0 { 68 | return core::ptr::null_mut(); 69 | } 70 | let layout = core::alloc::Layout::from_size_align_unchecked(size as usize, 0); 71 | alloc::alloc::alloc(layout) 72 | } 73 | 74 | #[no_mangle] 75 | pub unsafe extern "C" fn stateroom_free(ptr: *mut u8, size: u32) { 76 | if size == 0 { 77 | return; 78 | } 79 | let layout = core::alloc::Layout::from_size_align_unchecked(size as usize, 0); 80 | alloc::alloc::dealloc(ptr, layout); 81 | } 82 | } 83 | } 84 | } 85 | 86 | /// Exposes a `stateroom_wasm::StateroomService`-implementing trait as a WebAssembly module. 87 | #[proc_macro_attribute] 88 | pub fn stateroom_wasm(_attr: TokenStream, item: TokenStream) -> TokenStream { 89 | #[allow(clippy::needless_borrow)] 90 | stateroom_wasm_impl(&item.into()).into() 91 | } 92 | 93 | #[cfg(test)] 94 | mod test { 95 | use super::get_name; 96 | use quote::quote; 97 | 98 | #[test] 99 | fn test_parse_name() { 100 | assert_eq!( 101 | "MyStruct", 102 | get_name("e! { 103 | struct MyStruct {} 104 | }) 105 | .unwrap() 106 | .to_string() 107 | ); 108 | 109 | assert_eq!( 110 | "AnotherStruct", 111 | get_name("e! { 112 | struct AnotherStruct; 113 | }) 114 | .unwrap() 115 | .to_string() 116 | ); 117 | 118 | assert_eq!( 119 | "ATupleStruct", 120 | get_name("e! { 121 | struct ATupleStruct(u32, u32, u32); 122 | }) 123 | .unwrap() 124 | .to_string() 125 | ); 126 | 127 | assert_eq!( 128 | "AnEnum", 129 | get_name("e! { 130 | enum AnEnum { 131 | Option1, 132 | Option2(u32), 133 | } 134 | }) 135 | .unwrap() 136 | .to_string() 137 | ); 138 | 139 | assert_eq!( 140 | "ATypeDecl", 141 | get_name("e! { 142 | type ATypeDecl = u32; 143 | }) 144 | .unwrap() 145 | .to_string() 146 | ); 147 | 148 | assert!(get_name("e! { 149 | impl Foo {} 150 | }) 151 | .is_none()); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /stateroom/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stateroom" 3 | version = "0.4.4" 4 | authors = ["Paul Butler "] 5 | edition = "2018" 6 | readme = "../README.md" 7 | repository = "https://github.com/drifting-in-space/stateroom" 8 | license = "MIT OR Apache-2.0" 9 | keywords = ["websocket"] 10 | description = "A lightweight framework for building WebSocket services." 11 | 12 | [dependencies] 13 | serde = { version = "1.0.133", features = ["derive"], optional=true } 14 | 15 | [features] 16 | default = [] 17 | -------------------------------------------------------------------------------- /stateroom/src/client_id.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "serde")] 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Hash, Ord)] 5 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 6 | 7 | pub struct ClientId(pub u32); 8 | 9 | impl From for u32 { 10 | fn from(c: ClientId) -> Self { 11 | c.0 12 | } 13 | } 14 | 15 | impl From for ClientId { 16 | fn from(u: u32) -> Self { 17 | ClientId(u) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /stateroom/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Stateroom is a minimalist framework for developing stateful 2 | //! [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) applications. 3 | //! 4 | //! Stateroom apps implement the [SimpleStateroomService] trait, which provides a way for the 5 | //! app to hook into events when clients connect, disconnect, and send messages. Additionally, 6 | //! Stateroom provides a simple mechanism for invoking events in the future with a timer. 7 | //! 8 | //! A simple chat server looks like this: 9 | //! 10 | //! ``` 11 | //! use stateroom::*; 12 | //! use std::collections::HashMap; 13 | //! 14 | //! #[derive(Default)] 15 | //! struct ChatServer { 16 | //! /// The server's only state is a mapping of client ID to username. 17 | //! client_to_nickname: HashMap, 18 | //! } 19 | //! 20 | //! impl StateroomService for ChatServer { 21 | //! /// This is called when a user connects. 22 | //! fn connect(&mut self, client: ClientId, ctx: &impl StateroomContext) { 23 | //! let username = format!("client{}", u32::from(client)); 24 | //! 25 | //! // Send a welcome message. 26 | //! ctx.send_message(client, 27 | //! format!("Welcome to the chat! Your name is {}. \ 28 | //! Send /nick to change it.", 29 | //! &username)); 30 | //! 31 | //! // Alert all other connected users to the new user. 32 | //! ctx.send_message(MessageRecipient::Broadcast, 33 | //! format!("{} has joined the chat", &username)); 34 | //! } 35 | //! 36 | //! /// This is called when a user disconnects. 37 | //! fn disconnect(&mut self, client: ClientId, ctx: &impl StateroomContext) { 38 | //! let username = self.client_to_nickname.remove(&client).unwrap(); 39 | //! 40 | //! // Alert all remaining users that a user has left. 41 | //! ctx.send_message(MessageRecipient::Broadcast, 42 | //! format!("{} has left the chat", &username)); 43 | //! } 44 | //! 45 | //! /// This is called when a user sends a message. 46 | //! fn message(&mut self, client: ClientId, message: MessagePayload, ctx: &impl StateroomContext) { 47 | //! let Some(message) = message.text() else { 48 | //! // Ignore binary messages. 49 | //! return; 50 | //! }; 51 | //! 52 | //! if let Some(new_nick) = message.strip_prefix("/nick ") { 53 | //! // This message is a /nick command, so process accordingly. 54 | //! let old_nick = self.client_to_nickname.insert(client, new_nick.to_string()).unwrap(); 55 | //! ctx.send_message(MessageRecipient::Broadcast, 56 | //! format!("{} is now known as {}", old_nick, new_nick)); 57 | //! } else { 58 | //! // Broadcast the message to all connected users, prefixed by the username. 59 | //! let username = self.client_to_nickname.get(&client).unwrap(); 60 | //! ctx.send_message(MessageRecipient::Broadcast, 61 | //! format!("{}: {}", username, message)); 62 | //! } 63 | //! } 64 | //! } 65 | 66 | use std::{convert::Infallible, sync::Arc}; 67 | 68 | pub use client_id::ClientId; 69 | pub use message_recipient::MessageRecipient; 70 | pub use messages::{MessageFromProcess, MessagePayload, MessageToProcess}; 71 | 72 | mod client_id; 73 | mod message_recipient; 74 | mod messages; 75 | 76 | /// Provides an interface for a [StateroomService] instance to send messages back to its host environment. 77 | pub trait StateroomContext: Send + Sync + Clone + 'static { 78 | /// Sends a message to a currently connected user, or broadcast a message to all users. 79 | /// 80 | /// Recipient can be a `u32` representing an individual user to send a message to, or 81 | /// `MessageRecipient::Broadcast` to broadcast a message to all connected users. 82 | /// The message is a string which is sent verbatim to the user(s) indicated. 83 | fn send_message( 84 | &self, 85 | recipient: impl Into, 86 | message: impl Into, 87 | ); 88 | 89 | /// Sets a timer to wake up the service in the given number of milliseconds by invoking `timer()`. 90 | /// 91 | /// Each instance of a service can only have one (or zero) timer outstanding at any time; if this 92 | /// is called before an existing timer expires, the previous timer is replaced. This provides a 93 | /// very basic primitive that more complex timer behavior can be built on, using state and logic 94 | /// stored in your service. For example, you could implement multiple concurrent timers using a 95 | /// priority queue and ensuring that the environment timer always reflects the head of the queue. 96 | fn set_timer(&self, ms_delay: u32); 97 | } 98 | 99 | /// A simplified interface for creating a [StateroomService] that can be exposed as a WebAssembly module. 100 | /// 101 | /// See module documentation for usage examples. 102 | #[allow(unused_variables)] 103 | pub trait StateroomService: Send + Sync + 'static { 104 | /// Called when the service is created, before any client has had a chance to connect. 105 | fn init(&mut self, context: &impl StateroomContext) {} 106 | 107 | /// Called each time a client connects to the service. 108 | fn connect(&mut self, client: ClientId, context: &impl StateroomContext) {} 109 | 110 | /// Called each time a client disconnects from the service, unless that disconnection 111 | /// will cause the service to be destroyed. 112 | fn disconnect(&mut self, client: ClientId, context: &impl StateroomContext) {} 113 | 114 | /// Called each time a client sends a text message to the service. 115 | fn message( 116 | &mut self, 117 | client: ClientId, 118 | message: MessagePayload, 119 | context: &impl StateroomContext, 120 | ) { 121 | } 122 | 123 | /// Called when [StateroomContext::set_timer] has been called on this service's context, 124 | /// after the provided duration. 125 | fn timer(&mut self, context: &impl StateroomContext) {} 126 | } 127 | 128 | pub trait StateroomServiceFactory: Send + Sync + 'static { 129 | /// The type of [StateroomService] that the object implementing this trait builds. 130 | type Service: StateroomService; 131 | type Error: std::fmt::Debug; 132 | 133 | /// Non-destructively build a [StateroomService] from `self`. 134 | fn build( 135 | &self, 136 | room_id: &str, 137 | context: Arc, 138 | ) -> Result; 139 | } 140 | 141 | #[derive(Default)] 142 | pub struct DefaultStateroomFactory { 143 | _marker: std::marker::PhantomData, 144 | } 145 | 146 | impl StateroomServiceFactory for DefaultStateroomFactory { 147 | type Service = T; 148 | type Error = Infallible; 149 | 150 | fn build(&self, _: &str, _: Arc) -> Result { 151 | Ok(T::default()) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /stateroom/src/message_recipient.rs: -------------------------------------------------------------------------------- 1 | use crate::ClientId; 2 | #[cfg(feature = "serde")] 3 | use serde::{Deserialize, Serialize}; 4 | 5 | /// Represents the recipient(s) of a message. 6 | /// 7 | /// Messages may either be sent to a particular client by numeric id 8 | /// (`MessageRecipient::Client(3)`), or be broadcast to all connected clients 9 | /// (`MessageRecipient::Broadcast`).] 10 | #[derive(Debug, Clone, PartialEq)] 11 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 12 | 13 | pub enum MessageRecipient { 14 | Broadcast, 15 | Client(ClientId), 16 | EveryoneExcept(ClientId), 17 | } 18 | 19 | impl MessageRecipient { 20 | #[must_use] 21 | pub fn encode_i32(&self) -> i32 { 22 | match self { 23 | Self::Broadcast => 0, 24 | Self::Client(c) => c.0 as i32, 25 | Self::EveryoneExcept(c) => -(c.0 as i32), 26 | } 27 | } 28 | 29 | #[must_use] 30 | pub fn decode_i32(enc_client_id: i32) -> Self { 31 | match enc_client_id { 32 | 0 => Self::Broadcast, 33 | c if c > 0 => Self::Client((c as u32).into()), 34 | c => Self::EveryoneExcept((-c as u32).into()), 35 | } 36 | } 37 | } 38 | 39 | impl From for MessageRecipient { 40 | fn from(c: ClientId) -> Self { 41 | MessageRecipient::Client(c) 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use crate::{ClientId, MessageRecipient}; 48 | 49 | #[test] 50 | fn test_decode() { 51 | assert_eq!(MessageRecipient::Broadcast, MessageRecipient::decode_i32(0)); 52 | assert_eq!( 53 | MessageRecipient::Client(3.into()), 54 | MessageRecipient::decode_i32(3) 55 | ); 56 | assert_eq!( 57 | MessageRecipient::Client(9.into()), 58 | ClientId::from(9u32).into() 59 | ); 60 | 61 | assert_eq!(0, MessageRecipient::Broadcast.encode_i32()); 62 | assert_eq!(443, MessageRecipient::Client(443.into()).encode_i32()); 63 | 64 | assert_eq!(-4, MessageRecipient::EveryoneExcept(4.into()).encode_i32()); 65 | assert_eq!( 66 | MessageRecipient::EveryoneExcept(119.into()), 67 | MessageRecipient::decode_i32(-119) 68 | ); 69 | 70 | assert_eq!( 71 | MessageRecipient::EveryoneExcept(1.into()), 72 | MessageRecipient::decode_i32(-1) 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /stateroom/src/messages.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "serde")] 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::{ClientId, MessageRecipient}; 5 | 6 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 7 | #[derive(Debug)] 8 | pub enum MessagePayload { 9 | Bytes(Vec), 10 | Text(String), 11 | } 12 | 13 | impl MessagePayload { 14 | pub fn text(&self) -> Option<&str> { 15 | match self { 16 | MessagePayload::Text(s) => Some(s), 17 | _ => None, 18 | } 19 | } 20 | 21 | pub fn bytes(&self) -> Option<&[u8]> { 22 | match self { 23 | MessagePayload::Bytes(b) => Some(b), 24 | _ => None, 25 | } 26 | } 27 | } 28 | 29 | impl From for MessagePayload { 30 | fn from(val: String) -> Self { 31 | MessagePayload::Text(val) 32 | } 33 | } 34 | 35 | impl From<&str> for MessagePayload { 36 | fn from(val: &str) -> Self { 37 | MessagePayload::Text(val.to_string()) 38 | } 39 | } 40 | 41 | impl From> for MessagePayload { 42 | fn from(val: Vec) -> Self { 43 | MessagePayload::Bytes(val) 44 | } 45 | } 46 | 47 | impl From<&[u8]> for MessagePayload { 48 | fn from(val: &[u8]) -> Self { 49 | MessagePayload::Bytes(val.to_vec()) 50 | } 51 | } 52 | 53 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 54 | #[derive(Debug)] 55 | pub enum MessageToProcess { 56 | Init, 57 | Connect { 58 | client: ClientId, 59 | }, 60 | Disconnect { 61 | client: ClientId, 62 | }, 63 | Message { 64 | sender: ClientId, 65 | message: MessagePayload, 66 | }, 67 | Timer, 68 | } 69 | 70 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 71 | pub enum MessageFromProcess { 72 | Message { 73 | recipient: MessageRecipient, 74 | message: MessagePayload, 75 | }, 76 | SetTimer { 77 | ms_delay: u32, 78 | }, 79 | } 80 | --------------------------------------------------------------------------------