From 964a0d62158fac01ff9688e9128cd6f834ad06c6 Mon Sep 17 00:00:00 2001 From: sakex Date: Fri, 21 Jul 2023 12:05:32 +0200 Subject: [PATCH 01/28] rust-wasm-template --- .devcontainer/devcontainer.json | 13 + Cargo.lock | 1278 +++++++++++++++-- Cargo.toml | 2 + src/rust-wasm-template/README.md | 49 + src/rust-wasm-template/project/Cargo.toml | 19 + src/rust-wasm-template/project/src/lib.rs | 25 + src/rust-wasm-template/server/Cargo.toml | 10 + .../server/files/index.html | 97 ++ src/rust-wasm-template/server/files/index.mjs | 8 + .../server/files/source-code-regular.otf | Bin 0 -> 132740 bytes src/rust-wasm-template/server/src/main.rs | 50 + 11 files changed, 1467 insertions(+), 84 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 src/rust-wasm-template/README.md create mode 100644 src/rust-wasm-template/project/Cargo.toml create mode 100644 src/rust-wasm-template/project/src/lib.rs create mode 100644 src/rust-wasm-template/server/Cargo.toml create mode 100644 src/rust-wasm-template/server/files/index.html create mode 100644 src/rust-wasm-template/server/files/index.mjs create mode 100644 src/rust-wasm-template/server/files/source-code-regular.otf create mode 100644 src/rust-wasm-template/server/src/main.rs diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000000..1caf9d09b51 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,13 @@ +{ + "image": "mcr.microsoft.com/devcontainers/rust:0-1-bullseye", + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + "settings": {}, + "extensions": [ + "streetsidesoftware.code-spell-checker" + ] + } + } +} diff --git a/Cargo.lock b/Cargo.lock index 8b65dd36403..722a56eded0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,293 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "actix-codec" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78d1833b3838dbe990df0f1f87baf640cf6146e898166afe401839d1b001e570" +dependencies = [ + "bitflags", + "bytes 0.5.6", + "futures-core", + "futures-sink", + "log", + "pin-project 0.4.30", + "tokio 0.2.25", + "tokio-util 0.3.1", +] + +[[package]] +name = "actix-connect" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177837a10863f15ba8d3ae3ec12fac1099099529ed20083a27fdfe247381d0dc" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "derive_more", + "either", + "futures-util", + "http", + "log", + "trust-dns-proto", + "trust-dns-resolver", +] + +[[package]] +name = "actix-files" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51e8a9146c12fce92a6e4c24b8c4d9b05268130bfd8d61bc587e822c32ce689" +dependencies = [ + "actix-service", + "actix-web", + "bitflags", + "bytes 0.5.6", + "derive_more", + "futures-core", + "futures-util", + "log", + "mime", + "mime_guess", + "percent-encoding", + "v_htmlescape", +] + +[[package]] +name = "actix-http" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2be6b66b62a794a8e6d366ac9415bb7d475ffd1e9f4671f38c1d8a8a5df950b3" +dependencies = [ + "actix-codec", + "actix-connect", + "actix-rt", + "actix-service", + "actix-threadpool", + "actix-utils", + "base64 0.13.1", + "bitflags", + "brotli", + "bytes 0.5.6", + "cookie", + "copyless", + "derive_more", + "either", + "encoding_rs", + "flate2", + "futures-channel", + "futures-core", + "futures-util", + "fxhash", + "h2 0.2.7", + "http", + "httparse", + "indexmap", + "itoa 0.4.8", + "language-tags", + "lazy_static", + "log", + "mime", + "percent-encoding", + "pin-project 1.0.12", + "rand 0.7.3", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "sha-1", + "slab", + "time", +] + +[[package]] +name = "actix-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "actix-router" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad299af73649e1fc893e333ccf86f377751eb95ff875d095131574c6f43452c" +dependencies = [ + "bytestring", + "http", + "log", + "regex", + "serde", +] + +[[package]] +name = "actix-rt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227" +dependencies = [ + "actix-macros", + "actix-threadpool", + "copyless", + "futures-channel", + "futures-util", + "smallvec", + "tokio 0.2.25", +] + +[[package]] +name = "actix-server" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45407e6e672ca24784baa667c5d32ef109ccdd8d5e0b5ebb9ef8a67f4dfb708e" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "futures-channel", + "futures-util", + "log", + "mio 0.6.23", + "mio-uds", + "num_cpus", + "slab", + "socket2 0.3.19", +] + +[[package]] +name = "actix-service" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0052435d581b5be835d11f4eb3bce417c8af18d87ddf8ace99f8e67e595882bb" +dependencies = [ + "futures-util", + "pin-project 0.4.30", +] + +[[package]] +name = "actix-testing" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47239ca38799ab74ee6a8a94d1ce857014b2ac36f242f70f3f75a66f691e791c" +dependencies = [ + "actix-macros", + "actix-rt", + "actix-server", + "actix-service", + "log", + "socket2 0.3.19", +] + +[[package]] +name = "actix-threadpool" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d209f04d002854b9afd3743032a27b066158817965bf5d036824d19ac2cc0e30" +dependencies = [ + "derive_more", + "futures-channel", + "lazy_static", + "log", + "num_cpus", + "parking_lot 0.11.2", + "threadpool", +] + +[[package]] +name = "actix-tls" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24789b7d7361cf5503a504ebe1c10806896f61e96eca9a7350e23001aca715fb" +dependencies = [ + "actix-codec", + "actix-service", + "actix-utils", + "futures-util", +] + +[[package]] +name = "actix-utils" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9022dec56632d1d7979e59af14f0597a28a830a9c1c7fec8b2327eb9f16b5a" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "bitflags", + "bytes 0.5.6", + "either", + "futures-channel", + "futures-sink", + "futures-util", + "log", + "pin-project 0.4.30", + "slab", +] + +[[package]] +name = "actix-web" +version = "3.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6534a126df581caf443ba2751cab42092c89b3f1d06a9d829b1e17edfe3e277" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-testing", + "actix-threadpool", + "actix-tls", + "actix-utils", + "actix-web-codegen", + "awc", + "bytes 0.5.6", + "derive_more", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "fxhash", + "log", + "mime", + "pin-project 1.0.12", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "socket2 0.3.19", + "time", + "tinyvec", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad26f77093333e0e7c6ffe54ebe3582d908a104e448723eec6d43d08b07143fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "0.7.20" @@ -11,6 +298,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-example" version = "0.1.0" @@ -86,6 +388,17 @@ version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +[[package]] +name = "async-trait" +version = "0.1.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2d0f03b3640e3a630367e40c468cb7f309529c708ed1d88597047b0e7c6ef7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + [[package]] name = "atty" version = "0.2.14" @@ -94,7 +407,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi 0.1.19", "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -103,6 +416,36 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "awc" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b381e490e7b0cfc37ebc54079b0413d8093ef43d14a4e4747083f7fa47a9e691" +dependencies = [ + "actix-codec", + "actix-http", + "actix-rt", + "actix-service", + "base64 0.13.1", + "bytes 0.5.6", + "cfg-if 1.0.0", + "derive_more", + "futures-core", + "log", + "mime", + "percent-encoding", + "rand 0.7.3", + "serde", + "serde_json", + "serde_urlencoded", +] + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + [[package]] name = "base64" version = "0.13.1" @@ -121,6 +464,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -130,6 +482,27 @@ dependencies = [ "generic-array", ] +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bstr" version = "1.4.0" @@ -151,6 +524,15 @@ dependencies = [ "spin", ] +[[package]] +name = "buf-min" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa17aa1cf56bdd6bb30518767d00e58019d326f3f05d8c3e0730b549d332ea83" +dependencies = [ + "bytes 0.5.6", +] + [[package]] name = "bumpalo" version = "3.12.0" @@ -163,18 +545,39 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + [[package]] name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +[[package]] +name = "bytestring" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238e4886760d98c4f899360c834fa93e62cf7f721ac3c2da375cbdf4b8679aae" +dependencies = [ + "bytes 1.4.0", +] + [[package]] name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -187,7 +590,7 @@ version = "0.1.0" dependencies = [ "futures-util", "http", - "tokio", + "tokio 1.28.1", "tokio-websockets", ] @@ -200,7 +603,7 @@ dependencies = [ "iana-time-zone", "num-integer", "num-traits", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -277,12 +680,45 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen", +] + +[[package]] +name = "const_fn" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" + [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "cookie" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" +dependencies = [ + "percent-encoding", + "time", + "version_check 0.9.4", +] + +[[package]] +name = "copyless" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" + [[package]] name = "core-foundation" version = "0.9.3" @@ -308,13 +744,22 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "crossbeam-channel" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", ] @@ -324,7 +769,7 @@ version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -345,7 +790,7 @@ checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" dependencies = [ "cssparser-macros", "dtoa-short", - "itoa", + "itoa 1.0.6", "matches", "phf 0.10.1", "proc-macro2", @@ -417,20 +862,35 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.0", "syn 1.0.109", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "crypto-common", ] +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + [[package]] name = "dtoa" version = "0.4.8" @@ -452,6 +912,12 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591" +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "elasticlunr-rs" version = "3.0.2" @@ -470,7 +936,19 @@ version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", +] + +[[package]] +name = "enum-as-inner" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "570d109b813e904becc80d8d5da38376818a143348413f7149f1340fe04754d4" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -535,12 +1013,22 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall 0.2.16", "windows-sys 0.45.0", ] +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -580,6 +1068,22 @@ dependencies = [ "libc", ] +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + [[package]] name = "futf" version = "0.1.5" @@ -590,6 +1094,20 @@ dependencies = [ "new_debug_unreachable", ] +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.28" @@ -641,13 +1159,14 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ + "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", - "pin-project-lite", + "pin-project-lite 0.2.9", "pin-utils", "slab", ] @@ -668,7 +1187,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", - "version_check", + "version_check 0.9.4", ] [[package]] @@ -686,7 +1205,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -697,7 +1216,7 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi 0.11.0+wasi-snapshot-preview1", ] @@ -717,13 +1236,33 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "h2" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" +dependencies = [ + "bytes 0.5.6", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio 0.2.25", + "tokio-util 0.3.1", + "tracing", + "tracing-futures", +] + [[package]] name = "h2" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" dependencies = [ - "bytes", + "bytes 1.4.0", "fnv", "futures-core", "futures-sink", @@ -731,8 +1270,8 @@ dependencies = [ "http", "indexmap", "slab", - "tokio", - "tokio-util", + "tokio 1.28.1", + "tokio-util 0.7.7", "tracing", ] @@ -764,12 +1303,12 @@ checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.1", "bitflags", - "bytes", + "bytes 1.4.0", "headers-core", "http", "httpdate", "mime", - "sha1", + "sha1 0.10.5", ] [[package]] @@ -781,6 +1320,12 @@ dependencies = [ "http", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -805,6 +1350,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi 0.3.9", +] + [[package]] name = "html5ever" version = "0.26.0" @@ -825,9 +1381,9 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ - "bytes", + "bytes 1.4.0", "fnv", - "itoa", + "itoa 1.0.6", ] [[package]] @@ -836,9 +1392,9 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes", + "bytes 1.4.0", "http", - "pin-project-lite", + "pin-project-lite 0.2.9", ] [[package]] @@ -874,19 +1430,19 @@ version = "0.14.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" dependencies = [ - "bytes", + "bytes 1.4.0", "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.17", "http", "http-body", "httparse", "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", + "itoa 1.0.6", + "pin-project-lite 0.2.9", + "socket2 0.4.9", + "tokio 1.28.1", "tower-service", "tracing", "want", @@ -898,10 +1454,10 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes", + "bytes 1.4.0", "hyper", "native-tls", - "tokio", + "tokio 1.28.1", "tokio-native-tls", ] @@ -929,6 +1485,17 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.3.0" @@ -975,7 +1542,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -989,6 +1556,27 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "ipconfig" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" +dependencies = [ + "socket2 0.3.19", + "widestring", + "winapi 0.3.9", + "winreg 0.6.2", +] + [[package]] name = "ipnet" version = "2.7.2" @@ -1007,6 +1595,12 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + [[package]] name = "itoa" version = "1.0.6" @@ -1022,6 +1616,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "kqueue" version = "1.0.7" @@ -1042,6 +1646,12 @@ dependencies = [ "libc", ] +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" + [[package]] name = "lazy_static" version = "1.4.0" @@ -1063,6 +1673,12 @@ dependencies = [ "cc", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.3.1" @@ -1085,7 +1701,16 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", ] [[package]] @@ -1114,6 +1739,12 @@ dependencies = [ "tendril", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matches" version = "0.1.10" @@ -1148,7 +1779,7 @@ dependencies = [ "serde_json", "shlex", "tempfile", - "tokio", + "tokio 1.28.1", "toml", "topological-sort", "warp", @@ -1187,6 +1818,34 @@ dependencies = [ "unicase", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + [[package]] name = "mio" version = "0.8.6" @@ -1199,6 +1858,29 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio 0.6.23", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -1217,6 +1899,17 @@ dependencies = [ "tempfile", ] +[[package]] +name = "net2" +version = "0.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -1229,6 +1922,16 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "nom" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +dependencies = [ + "memchr", + "version_check 0.1.5", +] + [[package]] name = "notify" version = "5.1.0" @@ -1242,7 +1945,7 @@ dependencies = [ "inotify", "kqueue", "libc", - "mio", + "mio 0.8.6", "walkdir", "windows-sys 0.42.0", ] @@ -1292,6 +1995,12 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "opener" version = "0.5.2" @@ -1299,7 +2008,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005" dependencies = [ "bstr", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1309,7 +2018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ "bitflags", - "cfg-if", + "cfg-if 1.0.0", "foreign-types", "libc", "once_cell", @@ -1346,6 +2055,17 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -1353,7 +2073,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.7", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi 0.3.9", ] [[package]] @@ -1362,7 +2096,7 @@ version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall 0.2.16", "smallvec", @@ -1511,13 +2245,33 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" +dependencies = [ + "pin-project-internal 0.4.30", +] + [[package]] name = "pin-project" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" dependencies = [ - "pin-project-internal", + "pin-project-internal 1.0.12", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -1531,6 +2285,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "pin-project-lite" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -1586,6 +2346,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "project" +version = "0.1.0" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", +] + [[package]] name = "pulldown-cmark" version = "0.9.2" @@ -1741,11 +2511,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" dependencies = [ "base64 0.21.0", - "bytes", + "bytes 1.4.0", "encoding_rs", "futures-core", "futures-util", - "h2", + "h2 0.3.17", "http", "http-body", "hyper", @@ -1757,18 +2527,37 @@ dependencies = [ "native-tls", "once_cell", "percent-encoding", - "pin-project-lite", + "pin-project-lite 0.2.9", "serde", "serde_json", "serde_urlencoded", - "tokio", + "tokio 1.28.1", "tokio-native-tls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "winreg 0.10.1", +] + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", ] [[package]] @@ -1777,7 +2566,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver", + "semver 1.0.17", ] [[package]] @@ -1902,12 +2691,27 @@ dependencies = [ "smallvec", ] +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.159" @@ -1934,7 +2738,7 @@ version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" dependencies = [ - "itoa", + "itoa 1.0.6", "ryu", "serde", ] @@ -1946,11 +2750,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa", + "itoa 1.0.6", "ryu", "serde", ] +[[package]] +name = "server" +version = "0.1.0" +dependencies = [ + "actix-files", + "actix-web", +] + [[package]] name = "servo_arc" version = "0.2.0" @@ -1961,15 +2773,37 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + [[package]] name = "sha1" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", - "digest", + "digest 0.10.6", ] [[package]] @@ -1984,9 +2818,9 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", - "digest", + "digest 0.10.6", ] [[package]] @@ -2025,6 +2859,17 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "winapi 0.3.9", +] + [[package]] name = "socket2" version = "0.4.9" @@ -2032,7 +2877,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2050,6 +2895,64 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "standback" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" +dependencies = [ + "version_check 0.9.4", +] + +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version 0.2.3", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn 1.0.109", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1 0.6.1", + "syn 1.0.109", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + [[package]] name = "string_cache" version = "0.8.7" @@ -2058,7 +2961,7 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ "new_debug_unreachable", "once_cell", - "parking_lot", + "parking_lot 0.12.1", "phf_shared 0.10.0", "precomputed-hash", "serde", @@ -2110,7 +3013,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", "rustix", @@ -2167,6 +3070,53 @@ dependencies = [ "syn 2.0.13", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "time" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" +dependencies = [ + "const_fn", + "libc", + "standback", + "stdweb", + "time-macros", + "version_check 0.9.4", + "winapi 0.3.9", +] + +[[package]] +name = "time-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "standback", + "syn 1.0.109", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -2182,6 +3132,26 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" +dependencies = [ + "bytes 0.5.6", + "futures-core", + "iovec", + "lazy_static", + "libc", + "memchr", + "mio 0.6.23", + "mio-uds", + "pin-project-lite 0.1.12", + "signal-hook-registry", + "slab", + "winapi 0.3.9", +] + [[package]] name = "tokio" version = "1.28.1" @@ -2189,14 +3159,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" dependencies = [ "autocfg", - "bytes", + "bytes 1.4.0", "libc", - "mio", + "mio 0.8.6", "num_cpus", - "parking_lot", - "pin-project-lite", + "parking_lot 0.12.1", + "pin-project-lite 0.2.9", "signal-hook-registry", - "socket2", + "socket2 0.4.9", "tokio-macros", "windows-sys 0.48.0", ] @@ -2219,7 +3189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", - "tokio", + "tokio 1.28.1", ] [[package]] @@ -2229,8 +3199,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" dependencies = [ "futures-core", - "pin-project-lite", - "tokio", + "pin-project-lite 0.2.9", + "tokio 1.28.1", ] [[package]] @@ -2241,21 +3211,35 @@ checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" dependencies = [ "futures-util", "log", - "tokio", + "tokio 1.28.1", "tungstenite", ] +[[package]] +name = "tokio-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +dependencies = [ + "bytes 0.5.6", + "futures-core", + "futures-sink", + "log", + "pin-project-lite 0.1.12", + "tokio 0.2.25", +] + [[package]] name = "tokio-util" version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ - "bytes", + "bytes 1.4.0", "futures-core", "futures-sink", - "pin-project-lite", - "tokio", + "pin-project-lite 0.2.9", + "tokio 1.28.1", "tracing", ] @@ -2266,14 +3250,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d1df20f31f428a351ec353699a82aad49360090e8f6320ac0dacf5def4f1573" dependencies = [ "base64 0.21.0", - "bytes", + "bytes 1.4.0", "fastrand", "futures-util", "http", "httparse", "sha1_smol", - "tokio", - "tokio-util", + "tokio 1.28.1", + "tokio-util 0.7.7", ] [[package]] @@ -2303,9 +3287,9 @@ version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "log", - "pin-project-lite", + "pin-project-lite 0.2.9", "tracing-core", ] @@ -2318,6 +3302,55 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project 1.0.12", + "tracing", +] + +[[package]] +name = "trust-dns-proto" +version = "0.19.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cad71a0c0d68ab9941d2fb6e82f8fb2e86d9945b94e1661dd0aaea2b88215a9" +dependencies = [ + "async-trait", + "cfg-if 1.0.0", + "enum-as-inner", + "futures", + "idna 0.2.3", + "lazy_static", + "log", + "rand 0.7.3", + "smallvec", + "thiserror", + "tokio 0.2.25", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.19.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710f593b371175db53a26d0b38ed2978fafb9e9e8d3868b1acd753ea18df0ceb" +dependencies = [ + "cfg-if 0.1.10", + "futures", + "ipconfig", + "lazy_static", + "log", + "lru-cache", + "resolv-conf", + "smallvec", + "thiserror", + "tokio 0.2.25", + "trust-dns-proto", +] + [[package]] name = "try-lock" version = "0.2.4" @@ -2332,12 +3365,12 @@ checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" dependencies = [ "base64 0.13.1", "byteorder", - "bytes", + "bytes 1.4.0", "http", "httparse", "log", "rand 0.8.5", - "sha1", + "sha1 0.10.5", "thiserror", "url", "utf-8", @@ -2361,7 +3394,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check", + "version_check 0.9.4", ] [[package]] @@ -2398,7 +3431,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", - "idna", + "idna 0.3.0", "percent-encoding", ] @@ -2414,12 +3447,50 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "v_escape" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e0ab5fab1db278a9413d2ea794cb66f471f898c5b020c3c394f6447625d9d4" +dependencies = [ + "buf-min", + "v_escape_derive", +] + +[[package]] +name = "v_escape_derive" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29769400af8b264944b851c961a4a6930e76604f59b1fcd51246bab6a296c8c" +dependencies = [ + "nom", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "v_htmlescape" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f9a8af610ad6f7fc9989c9d2590d9764bc61f294884e9ee93baa58795174572" +dependencies = [ + "cfg-if 1.0.0", + "v_escape", +] + [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" + [[package]] name = "version_check" version = "0.9.4" @@ -2452,7 +3523,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27e1a710288f0f91a98dd8a74f05b76a10768db245ce183edf64dc1afdc3016c" dependencies = [ - "bytes", + "bytes 1.4.0", "futures-channel", "futures-util", "headers", @@ -2462,16 +3533,16 @@ dependencies = [ "mime", "mime_guess", "percent-encoding", - "pin-project", + "pin-project 1.0.12", "rustls-pemfile", "scoped-tls", "serde", "serde_json", "serde_urlencoded", - "tokio", + "tokio 1.28.1", "tokio-stream", "tokio-tungstenite", - "tokio-util", + "tokio-util 0.7.7", "tower-service", "tracing", ] @@ -2494,7 +3565,9 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", + "serde", + "serde_json", "wasm-bindgen-macro", ] @@ -2519,7 +3592,7 @@ version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -2564,6 +3637,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "widestring" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.9" @@ -2574,6 +3659,12 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -2586,7 +3677,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2751,13 +3842,32 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winreg" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "winreg" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7f1b06da21c..44587da6345 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,6 @@ members = [ "src/bare-metal/useful-crates/allocator-example", "src/bare-metal/useful-crates/zerocopy-example", "src/exercises/concurrency/chat-async", + "src/rust-wasm-template/project", + "src/rust-wasm-template/server", ] diff --git a/src/rust-wasm-template/README.md b/src/rust-wasm-template/README.md new file mode 100644 index 00000000000..885226835f6 --- /dev/null +++ b/src/rust-wasm-template/README.md @@ -0,0 +1,49 @@ +# rust-wasm-template + +This repository contains the minimum amount of code needed to experiment with WebAssembly. Including a web server to serve the HTML and WASM as well as the javascript boilerplate needed to load WASM. + +- `/server` contains the Web server as well as the static files +- `/project` contains the Rust code + +## Installation + +Note: If you are running from a devcontainer, you only need to install wasm-pack. + +### Rust + +Recommended: +``` +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +Alternatively, see the [installation page](https://www.rust-lang.org/tools/install). + +### wasm-pack + +Recommended: +``` +curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh +``` + +Alternatively, see the [installation page](https://rustwasm.github.io/wasm-pack/installer/). + +## Run the local server + +``` +cd server + +cargo run +``` + +- On a devcontainer, go to `PORTS` and open the link under `Local Address` for Port `8080` +- Locally, visit http://localhost:8080 + +## Build WASM and copy target to the correct path + +``` +cd project + +wasm-pack build --target web && cp -r pkg ../server +``` + +This command needs to be re-run to view your latest changes. diff --git a/src/rust-wasm-template/project/Cargo.toml b/src/rust-wasm-template/project/Cargo.toml new file mode 100644 index 00000000000..3c0ca2d586e --- /dev/null +++ b/src/rust-wasm-template/project/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "project" +version = "0.1.0" +edition = "2021" +publish = false + + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"] } +wasm-bindgen-futures = "0.4.24" +js-sys = "0.3.51" +# The `console_error_panic_hook` crate provides better debugging of panics by +# logging them with `console.error`. This is great for development, but requires +# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for +# code size when deploying. +console_error_panic_hook = "0.1.7" diff --git a/src/rust-wasm-template/project/src/lib.rs b/src/rust-wasm-template/project/src/lib.rs new file mode 100644 index 00000000000..d0af5ebec15 --- /dev/null +++ b/src/rust-wasm-template/project/src/lib.rs @@ -0,0 +1,25 @@ +extern crate console_error_panic_hook; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + fn alert(s: &str); + + #[wasm_bindgen(js_namespace = console)] + pub fn log(s: &str); +} + +#[wasm_bindgen] +pub fn set_panic_hook() { + // Generates better error messages if our code ever panics. + // + // For more details see + // https://github.com/rustwasm/console_error_panic_hook#readme + console_error_panic_hook::set_once(); +} + +#[wasm_bindgen] +pub fn add(a: i32, b: i32) -> i32 { + a + b +} diff --git a/src/rust-wasm-template/server/Cargo.toml b/src/rust-wasm-template/server/Cargo.toml new file mode 100644 index 00000000000..a011d84e534 --- /dev/null +++ b/src/rust-wasm-template/server/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "server" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +actix-web = "3.3.2" +actix-files = "0.5.0" + diff --git a/src/rust-wasm-template/server/files/index.html b/src/rust-wasm-template/server/files/index.html new file mode 100644 index 00000000000..f9e9f161005 --- /dev/null +++ b/src/rust-wasm-template/server/files/index.html @@ -0,0 +1,97 @@ + + + + + WebAssembly Template + + + +

+ WebAssembly Template +

+

+ This repository contains the minimum amount of code needed to experiment with WebAssembly. Including a web server to serve the HTML and WASM as well as the javascript boilerplate needed to load WASM. +

+

+ Edit server/files/index.mjs to edit the Javascript. +

+

+WASM output: +

+ + + diff --git a/src/rust-wasm-template/server/files/index.mjs b/src/rust-wasm-template/server/files/index.mjs new file mode 100644 index 00000000000..15bc9a9836c --- /dev/null +++ b/src/rust-wasm-template/server/files/index.mjs @@ -0,0 +1,8 @@ +import init, {set_panic_hook, add} from '/wasm/project.js'; + +(async () => { + // Run the init method to initiate the WebAssembly module. + await init(); + set_panic_hook(); + document.querySelector("#wasmoutput").innerHTML = add(1, 2); +})(); diff --git a/src/rust-wasm-template/server/files/source-code-regular.otf b/src/rust-wasm-template/server/files/source-code-regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..f32e6bd628dd9277896272a5e63fd8f324cc304a GIT binary patch literal 132740 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�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�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

) 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 literal 0 HcmV?d00001 diff --git a/src/rust-wasm-template/server/src/main.rs b/src/rust-wasm-template/server/src/main.rs new file mode 100644 index 00000000000..0129b7f9b73 --- /dev/null +++ b/src/rust-wasm-template/server/src/main.rs @@ -0,0 +1,50 @@ +use actix_files::NamedFile; +use actix_web::{get, web, App, HttpServer, Result}; +use std::path::PathBuf; + +#[get("/")] +async fn index() -> Result { + let path: PathBuf = "./files/index.html".parse()?; + Ok(NamedFile::open(path)?) +} + +#[get("/files/{name}")] +async fn files(web::Path(name): web::Path) -> Result { + let path: PathBuf = format!("./files/{}", name).parse()?; + Ok(NamedFile::open(path)?) +} + +#[get("/font.otf")] +async fn font() -> Result { + let path: PathBuf = "./files/source-code-regular.otf".parse()?; + Ok(NamedFile::open(path)?) +} + +#[get("/wasm/{name}")] +async fn serve_wasm(web::Path(name): web::Path) -> Result { + let path: PathBuf = format!("./pkg/{}", name).parse()?; + Ok(NamedFile::open(path)?) +} + +#[get("/wasm/snippets/{snippet_name}/{name}")] +async fn serve_wasm_snippet( + web::Path((snippet_name, name)): web::Path<(String, String)>, +) -> Result { + let path: PathBuf = format!("./pkg/snippets/{}/{}", snippet_name, name).parse()?; + Ok(NamedFile::open(path)?) +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + HttpServer::new(|| { + App::new() + .service(index) + .service(files) + .service(serve_wasm) + .service(serve_wasm_snippet) + .service(font) + }) + .bind("127.0.0.1:8080")? + .run() + .await +} From b989324e95634ef290d6f7121c46c09be4c51765 Mon Sep 17 00:00:00 2001 From: sakex Date: Fri, 21 Jul 2023 14:08:02 +0200 Subject: [PATCH 02/28] WASM intro --- src/SUMMARY.md | 3 +++ src/webassembly.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/webassembly.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ddcd83b6151..e76c385f6f6 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -297,6 +297,9 @@ - [Dining Philosophers](exercises/concurrency/dining-philosophers-async.md) - [Broadcast Chat Application](exercises/concurrency/chat-app.md) +# WebAssembly +---- +- [WebAssembly basics](webassembly.md) # Final Words diff --git a/src/webassembly.md b/src/webassembly.md new file mode 100644 index 00000000000..c68a9b3aab9 --- /dev/null +++ b/src/webassembly.md @@ -0,0 +1,42 @@ +# WebAssembly basics + +WebAssembly (WASM) is a binary instruction format designed to bring more languages to web browsers (historically, Javascript was the only available choice.) WASM is designed as a portable target for compilation of high-level languages like Rust, enabling it to be used in web applications. WASM has also grown beyond its scope because it can be run in a number of use cases beyond the web, including [nodejs](https://nodejs.dev/en/learn/nodejs-with-webassembly/) or the [Ethereum WebAsssembly Runtime](https://ewasm.readthedocs.io/en/mkdocs/README/) + +Rust is often considered as one of the best languages to compile to WebAssembly due to its safety, performance, and rather expansive WASM ecosystem. + +# Setting up the environment + +WebAssembly needs to be run in a Browser or a VM, therefore we will use a different set up for this class. Please navigate to [rust-wasm-template](https://github.com/google/comprehensive-rust/tree/main/src/rust-wasm-template) to view the installation instructions. Feel free to either clone the repository to run it locally, or open a new [Codespace on Github](https://codespaces.new/google/comprehensive-rust) + +## Install wasm-pack + +Recommended: + +```shell +curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh +``` + +Alternatively, see the [installation page](https://rustwasm.github.io/wasm-pack/installer/). + +## Run the local server + +WebAssembly cannot be loaded from the `files://` protocol yet, so we need to spawn a Web server to serve the different files. + +```shell +cd server + +cargo run +``` + +- On a devcontainer, go to `PORTS` and open the link under `Local Address` for Port `8080` +- Locally, visit http://localhost:8080 + +## Build WASM and copy target to the correct path + +This command needs to be re-run to view your latest changes. + +```shell +cd project + +wasm-pack build --target web && cp -r pkg ../server +``` From b0ce243f86b41424e6e14963ea9976e882354c6a Mon Sep 17 00:00:00 2001 From: sakex Date: Fri, 21 Jul 2023 14:32:36 +0200 Subject: [PATCH 03/28] load-wasm-module --- src/SUMMARY.md | 1 + src/webassembly.md | 35 ++--------------------------- src/webassembly/load-wasm-module.md | 32 ++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 33 deletions(-) create mode 100644 src/webassembly/load-wasm-module.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index e76c385f6f6..75e3e96e55a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -300,6 +300,7 @@ # WebAssembly ---- - [WebAssembly basics](webassembly.md) + - [Load a WASM module](webassembly/load-wasm-module.md) # Final Words diff --git a/src/webassembly.md b/src/webassembly.md index c68a9b3aab9..dc9484c67a6 100644 --- a/src/webassembly.md +++ b/src/webassembly.md @@ -6,37 +6,6 @@ Rust is often considered as one of the best languages to compile to WebAssembly # Setting up the environment -WebAssembly needs to be run in a Browser or a VM, therefore we will use a different set up for this class. Please navigate to [rust-wasm-template](https://github.com/google/comprehensive-rust/tree/main/src/rust-wasm-template) to view the installation instructions. Feel free to either clone the repository to run it locally, or open a new [Codespace on Github](https://codespaces.new/google/comprehensive-rust) - -## Install wasm-pack - -Recommended: - -```shell -curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -``` - -Alternatively, see the [installation page](https://rustwasm.github.io/wasm-pack/installer/). - -## Run the local server - -WebAssembly cannot be loaded from the `files://` protocol yet, so we need to spawn a Web server to serve the different files. +There are multiple frameworks to create WASM libraries from Rust. We will focus on [wasm-pack](https://rustwasm.github.io/docs/wasm-pack/introduction.html) and [wasm-bindgen](https://rustwasm.github.io/wasm-bindgen/). -```shell -cd server - -cargo run -``` - -- On a devcontainer, go to `PORTS` and open the link under `Local Address` for Port `8080` -- Locally, visit http://localhost:8080 - -## Build WASM and copy target to the correct path - -This command needs to be re-run to view your latest changes. - -```shell -cd project - -wasm-pack build --target web && cp -r pkg ../server -``` +WebAssembly needs to be run in a Browser or a VM, therefore we will use a different set up for this class. Please navigate to [rust-wasm-template](https://github.com/google/comprehensive-rust/tree/main/src/rust-wasm-template) to view the installation instructions. Feel free to either clone the repository to run it locally, or open a new [Codespace on Github](https://codespaces.new/google/comprehensive-rust) diff --git a/src/webassembly/load-wasm-module.md b/src/webassembly/load-wasm-module.md new file mode 100644 index 00000000000..63bdb2fdf87 --- /dev/null +++ b/src/webassembly/load-wasm-module.md @@ -0,0 +1,32 @@ +# Load a WASM module + +Once you have compiled your WebAssembly module, you want to call it from your Web application. +This chapter will cover minimum amount of Javascript required to load a WASM module into a Web application. + +```javascript +// Import the module and the exported method `add` +import init, {add} from '/wasm/project.js'; // A typescript version is also generated + +// Async IIFE +(async () => { + // Run the init method to initiate the WebAssembly module. + await init(); + console.log('WASM output', add(1, 2)); +})(); +``` + +Adding the Javascript module to an HTML file: + +```html + +``` + +

+ +* This loads the compiled WebAssembly +* `init` installs the bytecode and compiles it +* `add` is an exported method +* For this class, we are compiling `wasm-pack` with the `--web` flag, complex applications will want to use a bundler, +see more information about build options on the [official documentation](https://rustwasm.github.io/docs/wasm-pack/commands/build.html) + +
From 7d209454b105bfac3219e94a7c0d88e3389a6a46 Mon Sep 17 00:00:00 2001 From: sakex Date: Fri, 21 Jul 2023 14:48:49 +0200 Subject: [PATCH 04/28] expose-method --- src/SUMMARY.md | 1 + .../server/files/index.html | 2 +- src/webassembly/expose-method.md | 23 ++++++++++++++++++ src/webassembly/load-wasm-module.md | 24 ++++++++++++++++++- 4 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 src/webassembly/expose-method.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 75e3e96e55a..2c578e9a5c2 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -301,6 +301,7 @@ ---- - [WebAssembly basics](webassembly.md) - [Load a WASM module](webassembly/load-wasm-module.md) + - [Expose a method](webassembly/expose-method.md) # Final Words diff --git a/src/rust-wasm-template/server/files/index.html b/src/rust-wasm-template/server/files/index.html index f9e9f161005..1876111d5ee 100644 --- a/src/rust-wasm-template/server/files/index.html +++ b/src/rust-wasm-template/server/files/index.html @@ -87,7 +87,7 @@

This repository contains the minimum amount of code needed to experiment with WebAssembly. Including a web server to serve the HTML and WASM as well as the javascript boilerplate needed to load WASM.

- Edit server/files/index.mjs to edit the Javascript. + Edit server/files/index.mjs to edit the Javascript.

WASM output: diff --git a/src/webassembly/expose-method.md b/src/webassembly/expose-method.md new file mode 100644 index 00000000000..5bd354dcb00 --- /dev/null +++ b/src/webassembly/expose-method.md @@ -0,0 +1,23 @@ +# Expose a method + +The first thing you will want to do with WebAssembly is expose your methods to Javascript. +This is straightforward using the `#[wasm_bindgen]` procedural macro. + +```rust +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn set_panic_hook() { + // Generates better error messages if our code ever panics. + // + // For more details see + // https://github.com/rustwasm/console_error_panic_hook#readme + console_error_panic_hook::set_once(); +} + +// Exposes the `add` method from the previous slide +#[wasm_bindgen] +pub fn add(a: i32, b: i32) -> i32 { + a + b +} +``` diff --git a/src/webassembly/load-wasm-module.md b/src/webassembly/load-wasm-module.md index 63bdb2fdf87..ac2e2502165 100644 --- a/src/webassembly/load-wasm-module.md +++ b/src/webassembly/load-wasm-module.md @@ -1,7 +1,29 @@ # Load a WASM module +## Commands to run: + +You can compile the basic WASM library provided in [rust-wasm-template](https://github.com/google/comprehensive-rust/tree/main/src/rust-wasm-template) with this command: + +```shell +cd src/rust-wasm-template/project + +wasm-pack build --target web && cp -r pkg ../server +``` + +You can start the web server provided in [rust-wasm-template](https://github.com/google/comprehensive-rust/tree/main/src/rust-wasm-template) with this command: + + +```shell +cd src/rust-wasm-template/server + +cargo run +``` + +Open the web page on port `8080`. HTML and JS files are provided at [rust-wasm-template/server](https://github.com/google/comprehensive-rust/tree/main/src/rust-wasm-template/server/files). + +## Javascript + Once you have compiled your WebAssembly module, you want to call it from your Web application. -This chapter will cover minimum amount of Javascript required to load a WASM module into a Web application. ```javascript // Import the module and the exported method `add` From fdf83866cb3e3f879b02a3a389c7483033b5f68c Mon Sep 17 00:00:00 2001 From: sakex Date: Fri, 21 Jul 2023 18:11:48 +0200 Subject: [PATCH 05/28] expose-rust-type --- src/SUMMARY.md | 2 + src/rust-wasm-template/server/src/main.rs | 10 +-- src/webassembly/borrow-checker.md | 61 +++++++++++++++++ src/webassembly/expose-method.md | 6 ++ src/webassembly/expose-rust-type.md | 79 +++++++++++++++++++++++ 5 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 src/webassembly/borrow-checker.md create mode 100644 src/webassembly/expose-rust-type.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 2c578e9a5c2..db99ccdb19e 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -302,6 +302,8 @@ - [WebAssembly basics](webassembly.md) - [Load a WASM module](webassembly/load-wasm-module.md) - [Expose a method](webassembly/expose-method.md) + - [Expose user-defined Rust types](webassembly/expose-rust-type.md) + - [Borrow Checker](webassembly/borrow-checker.md) # Final Words diff --git a/src/rust-wasm-template/server/src/main.rs b/src/rust-wasm-template/server/src/main.rs index 0129b7f9b73..fffe0528056 100644 --- a/src/rust-wasm-template/server/src/main.rs +++ b/src/rust-wasm-template/server/src/main.rs @@ -5,25 +5,25 @@ use std::path::PathBuf; #[get("/")] async fn index() -> Result { let path: PathBuf = "./files/index.html".parse()?; - Ok(NamedFile::open(path)?) + Ok(NamedFile::open(path)?.use_last_modified(true)) } #[get("/files/{name}")] async fn files(web::Path(name): web::Path) -> Result { let path: PathBuf = format!("./files/{}", name).parse()?; - Ok(NamedFile::open(path)?) + Ok(NamedFile::open(path)?.use_last_modified(true)) } #[get("/font.otf")] async fn font() -> Result { let path: PathBuf = "./files/source-code-regular.otf".parse()?; - Ok(NamedFile::open(path)?) + Ok(NamedFile::open(path)?.use_last_modified(true)) } #[get("/wasm/{name}")] async fn serve_wasm(web::Path(name): web::Path) -> Result { let path: PathBuf = format!("./pkg/{}", name).parse()?; - Ok(NamedFile::open(path)?) + Ok(NamedFile::open(path)?.use_last_modified(true)) } #[get("/wasm/snippets/{snippet_name}/{name}")] @@ -31,7 +31,7 @@ async fn serve_wasm_snippet( web::Path((snippet_name, name)): web::Path<(String, String)>, ) -> Result { let path: PathBuf = format!("./pkg/snippets/{}/{}", snippet_name, name).parse()?; - Ok(NamedFile::open(path)?) + Ok(NamedFile::open(path)?.use_last_modified(true)) } #[actix_web::main] diff --git a/src/webassembly/borrow-checker.md b/src/webassembly/borrow-checker.md new file mode 100644 index 00000000000..b79a932ecfe --- /dev/null +++ b/src/webassembly/borrow-checker.md @@ -0,0 +1,61 @@ +# Borrow Checker + +Types annotated with `wasm_bindgen` can reference each other. + +```rust +#[wasm_bindgen] +pub struct MultiCounter { + // We use the counter from the previous slide + counters: Vec, +} + +#[wasm_bindgen] +impl MultiCounter { + #[wasm_bindgen(constructor)] + pub fn new() -> MultiCounter { + MultiCounter { counters: Vec::new() } + } + + pub fn increment(&mut self) { + for counter in &mut self.counters { + counter.increment(); + } + } + + pub fn add_counter(&mut self, counter: Counter) { + self.counter.push(counter); + } +} +``` + +But what happens when you call `add_counter` from Javascript ? + +```javascript +import init, {set_panic_hook, Counter, MultiCounter} from '/wasm/project.js'; + +(async () => { + // Run the init method to initiate the WebAssembly module. + await init(); + set_panic_hook(); + const wasmOutput = document.querySelector("#wasmoutput"); + const button = document.querySelector("#button"); + + const counter = new Counter("ButtonCounter", 42); + counter.increment(); + // Works fine + wasmOutput.textContent = counter.count; + + const multiCounter = new MultiCounter(); + // Move counter into the MultiCounter + multiCounter.add_counter(counter); + // Error: Open console + counter.increment(); +})(); +``` + +

+ +* `counter` is moved before the second call, so the code panics +* Ownership rules must be respected + +
\ No newline at end of file diff --git a/src/webassembly/expose-method.md b/src/webassembly/expose-method.md index 5bd354dcb00..a1e765de1c9 100644 --- a/src/webassembly/expose-method.md +++ b/src/webassembly/expose-method.md @@ -21,3 +21,9 @@ pub fn add(a: i32, b: i32) -> i32 { a + b } ``` + +
+ +* `set_panic_hook` is a convenient setup method that adds debug information to stack traces when a WASM module panics. Don't use it in prod builds because it is rather + +
\ No newline at end of file diff --git a/src/webassembly/expose-rust-type.md b/src/webassembly/expose-rust-type.md new file mode 100644 index 00000000000..58e40291a8f --- /dev/null +++ b/src/webassembly/expose-rust-type.md @@ -0,0 +1,79 @@ +# Expose user-defined Rust types + +Similarily to methods, types can be exposed from Rust to Javascript with the `#[wasm_bindgen]` macro. + +Members that implement `Copy` can be public and directly accessed from Javascript. + +```rust +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub struct Counter { + name: String, + pub count: u8, +} +``` + +Methods can also be exported + +```rust +#[wasm_bindgen] +impl Counter { + // Constructor will be called in JS when using `new Counter(name, count)` + #[wasm_bindgen(constructor)] + pub fn new(name: String, count: u8) -> Counter { + Counter { name, count } + } + + pub fn increment(&mut self) { + self.count += 1; + } + + // Getter for the name + #[wasm_bindgen(getter)] + pub fn name(&self) -> String { + self.name.clone() + } + + // Setter for the name + #[wasm_bindgen(setter)] + pub fn set_name(&mut self, name: String) { + self.name = name; + } +} +``` + +Add this button to the HTML file + +```html + +``` + +Javascript to use the `Counter` + +```javascript +import init, { set_panic_hook, Counter } from "/wasm/project.js"; + +(async () => { + // Run the init method to initiate the WebAssembly module. + await init(); + set_panic_hook(); + const wasmOutput = document.querySelector("#wasmoutput"); + const button = document.querySelector("#button"); + const counter = new Counter("ButtonCounter", 42); + wasmOutput.textContent = counter.count; + button.addEventListener("click", () => { + // Calls the getter + console.log("Button clicked!", counter.name); + counter.increment(); + wasmOutput.textContent = counter.count; + }); +})(); +``` + +
+ +- `pub` members must implement copy +- Type parameters and lifetime annotations are not supported yet + +
From d4f4ff30baef6ed3388c1bc47a23e36ff9c08092 Mon Sep 17 00:00:00 2001 From: sakex Date: Fri, 21 Jul 2023 18:15:33 +0200 Subject: [PATCH 06/28] Fix summary indentation --- src/SUMMARY.md | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index db99ccdb19e..49cd82632b2 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -1,6 +1,7 @@ # Summary [Welcome to Comprehensive Rust 🦀](welcome.md) + - [Running the Course](running-the-course.md) - [Course Structure](running-the-course/course-structure.md) - [Keyboard Shortcuts](running-the-course/keyboard-shortcuts.md) @@ -10,10 +11,9 @@ - [Code Samples](cargo/code-samples.md) - [Running Cargo Locally](cargo/running-locally.md) - # Day 1: Morning ----- +--- - [Welcome](welcome-day-1.md) - [What is Rust?](welcome-day-1/what-is-rust.md) @@ -67,10 +67,9 @@ - [Storing Books](exercises/day-1/book-library.md) - [Iterators and Ownership](exercises/day-1/iterators-and-ownership.md) - # Day 2: Morning ----- +--- - [Welcome](welcome-day-2.md) - [Structs](structs.md) @@ -120,10 +119,9 @@ - [Luhn Algorithm](exercises/day-2/luhn.md) - [Strings and Iterators](exercises/day-2/strings-iterators.md) - # Day 3: Morning ----- +--- - [Welcome](welcome-day-3.md) - [Generics](generics.md) @@ -177,10 +175,9 @@ - [Exercises](exercises/day-3/afternoon.md) - [Safe FFI Wrapper](exercises/day-3/safe-ffi-wrapper.md) - # Android ----- +--- - [Welcome](android.md) - [Setup](android/setup.md) @@ -203,10 +200,9 @@ - [With Java](android/interoperability/java.md) - [Exercises](exercises/android/morning.md) - # Bare Metal: Morning ----- +--- - [Welcome](bare-metal.md) - [no_std](bare-metal/no_std.md) @@ -253,10 +249,9 @@ - [Exercises](exercises/bare-metal/afternoon.md) - [RTC Driver](exercises/bare-metal/rtc.md) - # Concurrency: Morning ----- +--- - [Welcome](concurrency.md) - [Threads](concurrency/threads.md) @@ -298,25 +293,26 @@ - [Broadcast Chat Application](exercises/concurrency/chat-app.md) # WebAssembly ----- + +--- + - [WebAssembly basics](webassembly.md) - - [Load a WASM module](webassembly/load-wasm-module.md) - - [Expose a method](webassembly/expose-method.md) - - [Expose user-defined Rust types](webassembly/expose-rust-type.md) - - [Borrow Checker](webassembly/borrow-checker.md) +- [Load a WASM module](webassembly/load-wasm-module.md) +- [Expose a method](webassembly/expose-method.md) +- [Expose user-defined Rust types](webassembly/expose-rust-type.md) +- [Borrow Checker](webassembly/borrow-checker.md) # Final Words ----- +--- - [Thanks!](thanks.md) - [Other Resources](other-resources.md) - [Credits](credits.md) - # Solutions ----- +--- - [Solutions](exercises/solutions.md) - [Day 1 Morning](exercises/day-1/solutions-morning.md) From 39ae389af10f9649972e4794cc6ef136381dcb83 Mon Sep 17 00:00:00 2001 From: sakex Date: Fri, 21 Jul 2023 18:41:47 +0200 Subject: [PATCH 07/28] import-method --- src/SUMMARY.md | 1 + src/webassembly/import-method.md | 48 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/webassembly/import-method.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 49cd82632b2..85dc4e048b4 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -298,6 +298,7 @@ - [WebAssembly basics](webassembly.md) - [Load a WASM module](webassembly/load-wasm-module.md) +- [Import Method](webassembly/import-method.md) - [Expose a method](webassembly/expose-method.md) - [Expose user-defined Rust types](webassembly/expose-rust-type.md) - [Borrow Checker](webassembly/borrow-checker.md) diff --git a/src/webassembly/import-method.md b/src/webassembly/import-method.md new file mode 100644 index 00000000000..a732e3e470a --- /dev/null +++ b/src/webassembly/import-method.md @@ -0,0 +1,48 @@ +# Import a Javascript method + +Since WASM runs in the browser, we will want to interact directly with Javascript APIs from Rust. +For instance `println!` will not log to the javascript console, so we need to use `console.log`. +Similarly, we want to be able to call `alert`. This works the same way as FFIs with C. + +```rust +#[wasm_bindgen] +extern "C" { + fn alert(s: &str); + + // `js_namespace` will get values inside of a nested object in window. Here, `window.console.log` + #[wasm_bindgen(js_namespace = console)] + pub fn log(s: &str); + + // jsMethod is a user defined method defined in the `window` object + pub fn jsMethod(); +} + +#[wasm_bindgen] +pub fn add_and_log(a: i32, b: i32) { + let result = a + b; + // Doesn't show in the console + println!("println! {}", result); + log(&format!("log {}", result)); + alert(&format!("alert {}", result)); + jsMethod("Hi from Rust"); +} +``` + +```javascript +import init, { add_and_log } from "/wasm/project.js"; + +export function jsMethod(value) { + alert(`Hello from JS! Value: ${value}`); +} + +window.jsMethod = jsMethod; + +(async () => { + await init(); + add_and_log(1, 2); +})(); +``` + +
+ +
\ No newline at end of file From 7d411b72377a4a4a9bc168b1cdb0b3261464ea72 Mon Sep 17 00:00:00 2001 From: sakex Date: Fri, 21 Jul 2023 19:12:33 +0200 Subject: [PATCH 08/28] web-sys --- Cargo.lock | 1 + src/SUMMARY.md | 1 + src/rust-wasm-template/project/Cargo.toml | 10 ++++++ src/webassembly/web-sys.md | 39 +++++++++++++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 src/webassembly/web-sys.md diff --git a/Cargo.lock b/Cargo.lock index 722a56eded0..92ab3d9e70e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2354,6 +2354,7 @@ dependencies = [ "js-sys", "wasm-bindgen", "wasm-bindgen-futures", + "web-sys", ] [[package]] diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 85dc4e048b4..033e9201dfb 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -299,6 +299,7 @@ - [WebAssembly basics](webassembly.md) - [Load a WASM module](webassembly/load-wasm-module.md) - [Import Method](webassembly/import-method.md) + - [web-sys](webassembly/web-sys.md) - [Expose a method](webassembly/expose-method.md) - [Expose user-defined Rust types](webassembly/expose-rust-type.md) - [Borrow Checker](webassembly/borrow-checker.md) diff --git a/src/rust-wasm-template/project/Cargo.toml b/src/rust-wasm-template/project/Cargo.toml index 3c0ca2d586e..23dfe90819c 100644 --- a/src/rust-wasm-template/project/Cargo.toml +++ b/src/rust-wasm-template/project/Cargo.toml @@ -17,3 +17,13 @@ js-sys = "0.3.51" # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for # code size when deploying. console_error_panic_hook = "0.1.7" + +[dependencies.web-sys] +version = "0.3.4" +features = [ + 'Document', + 'Element', + 'HtmlElement', + 'Node', + 'Window', +] diff --git a/src/webassembly/web-sys.md b/src/webassembly/web-sys.md new file mode 100644 index 00000000000..ff58948cdae --- /dev/null +++ b/src/webassembly/web-sys.md @@ -0,0 +1,39 @@ +# web-sys and js_sys + +You don't need to import all Javascript methods manually. [web_sys](https://rustwasm.github.io/wasm-bindgen/web-sys/index.html) amd [js_sys](https://docs.rs/js-sys/latest/js_sys/) import Javascript and DOM methods for us. + +```rust +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn add_a_cat() -> Result<(), JsValue> { + let window = web_sys::window().expect("no global `window` exists"); + let document = window.document().expect("should have a document on window"); + let body = document.body().expect("document should have a body"); + + let image = document.create_element("img")?; + // We need a random number to prevent the browser from caching the image + let random_number = js_sys::Math::random(); + image.set_attribute("src", &format!("https://cataas.com/cat?random={random_number}"))?; + body.append_child(&image)?; + + Ok(()) +} +``` + +```javascript +import init, {set_panic_hook, add_a_cat} from '/wasm/project.js'; + + +(async () => { + // Run the init method to initiate the WebAssembly module. + await init(); + set_panic_hook(); + const button = document.createElement("button"); + button.textContent = "Add a cat"; + document.body.appendChild(button); + button.addEventListener("click", () => { + add_a_cat(); + }); +})(); +``` \ No newline at end of file From 4f8bc9061fde033ddc09c410d6159cc0a45668a8 Mon Sep 17 00:00:00 2001 From: sakex Date: Mon, 24 Jul 2023 17:30:21 +0200 Subject: [PATCH 09/28] error-handling.md --- src/SUMMARY.md | 1 + src/webassembly/error-handling.md | 54 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/webassembly/error-handling.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 033e9201dfb..cdc8cf8856d 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -301,6 +301,7 @@ - [Import Method](webassembly/import-method.md) - [web-sys](webassembly/web-sys.md) - [Expose a method](webassembly/expose-method.md) +- [Error handling](webassembly/error-handling.md) - [Expose user-defined Rust types](webassembly/expose-rust-type.md) - [Borrow Checker](webassembly/borrow-checker.md) diff --git a/src/webassembly/error-handling.md b/src/webassembly/error-handling.md new file mode 100644 index 00000000000..37a6a47b35f --- /dev/null +++ b/src/webassembly/error-handling.md @@ -0,0 +1,54 @@ +# Error handling + +`Result` is translated to javascript errors. Methods directly exposed and called from Javascript that return a `Result` +will automatically throw an error if the variant of the return value is `Result::Err`. `E` has to implement `Into`. + +```rust +#[wasm_bindgen] +pub fn str_to_int(s: &str) -> Option { + s.parse::() + .map_err(|_| JsValue::from_str("Failed to parse string")) +} +``` + +`Option` is converted to `undefined` in case of `None`. + +```rust +#[wasm_bindgen] +pub fn str_to_int(s: &str) -> Option { + s.parse::().ok() +} + +``` + +Javascript (click on the wasm output box) to parse the string: + +```javascript +import init, {set_panic_hook, str_to_int} from '/wasm/project.js'; + + +(async () => { + // Run the init method to initiate the WebAssembly module. + await init(); + set_panic_hook(); + const wasmoutput = document.querySelector('#wasmoutput'); + const input = document.createElement('input'); + input.type = 'text'; + document.body.appendChild(input); + wasmoutput.onclick = () => { + try { + wasmoutput.innerHTML = str_to_int(input.value); + } catch (e) { + wasmoutput.innerHTML = e; + } + }; +})(); +``` + + +
+ +* Click on the wasm output box to see the output +* `?` and other error handling toos also work + +
\ No newline at end of file From 806335795ce8a6b13fc083bfd06f1254c40bfd59 Mon Sep 17 00:00:00 2001 From: sakex Date: Mon, 24 Jul 2023 19:27:41 +0200 Subject: [PATCH 10/28] improve CSS --- src/rust-wasm-template/server/files/index.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rust-wasm-template/server/files/index.html b/src/rust-wasm-template/server/files/index.html index 1876111d5ee..18e222fd0c4 100644 --- a/src/rust-wasm-template/server/files/index.html +++ b/src/rust-wasm-template/server/files/index.html @@ -60,7 +60,6 @@ } input, select { - float: right; width: 150px; padding: 5px; background-color: #31363f; @@ -75,6 +74,13 @@ padding: 10px; border-radius: 5px; font-weight: bold; + cursor: pointer; + } + + img { + width: 300px; + height: 300px; + object-fit: contain; } From 13af2243ecfc4261e19c6b1224e3e5e4205896fa Mon Sep 17 00:00:00 2001 From: sakex Date: Mon, 24 Jul 2023 19:47:27 +0200 Subject: [PATCH 11/28] import-js-type --- src/SUMMARY.md | 8 +- .../{ => expose-method}/error-handling.md | 6 +- src/webassembly/import-js-type.md | 89 +++++++++++++++++++ .../import-method/error-handling.md | 18 ++++ .../{ => import-method}/web-sys.md | 0 5 files changed, 115 insertions(+), 6 deletions(-) rename src/webassembly/{ => expose-method}/error-handling.md (87%) create mode 100644 src/webassembly/import-js-type.md create mode 100644 src/webassembly/import-method/error-handling.md rename src/webassembly/{ => import-method}/web-sys.md (100%) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index cdc8cf8856d..d531ba2d13d 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -298,11 +298,13 @@ - [WebAssembly basics](webassembly.md) - [Load a WASM module](webassembly/load-wasm-module.md) -- [Import Method](webassembly/import-method.md) - - [web-sys](webassembly/web-sys.md) - [Expose a method](webassembly/expose-method.md) -- [Error handling](webassembly/error-handling.md) + - [Error handling for exposed methods](webassembly/expose-method/error-handling.md) +- [Import Method](webassembly/import-method.md) + - [Error handling for imported methods](webassembly/import-method/error-handling.md) + - [web-sys](webassembly/import-method/web-sys.md) - [Expose user-defined Rust types](webassembly/expose-rust-type.md) +- [Import user-defined Javascript types](webassembly/import-js-type.md) - [Borrow Checker](webassembly/borrow-checker.md) # Final Words diff --git a/src/webassembly/error-handling.md b/src/webassembly/expose-method/error-handling.md similarity index 87% rename from src/webassembly/error-handling.md rename to src/webassembly/expose-method/error-handling.md index 37a6a47b35f..7014ff5ccbb 100644 --- a/src/webassembly/error-handling.md +++ b/src/webassembly/expose-method/error-handling.md @@ -5,7 +5,7 @@ will automatically throw an error if the variant of the return value is `Result: ```rust #[wasm_bindgen] -pub fn str_to_int(s: &str) -> Option { +pub fn str_to_int(s: &str) -> Result { s.parse::() .map_err(|_| JsValue::from_str("Failed to parse string")) } @@ -21,7 +21,7 @@ pub fn str_to_int(s: &str) -> Option { ``` -Javascript (click on the wasm output box) to parse the string: +Javascript, click on the wasm output box to parse the string: ```javascript import init, {set_panic_hook, str_to_int} from '/wasm/project.js'; @@ -49,6 +49,6 @@ import init, {set_panic_hook, str_to_int} from '/wasm/project.js';
* Click on the wasm output box to see the output -* `?` and other error handling toos also work +* `?` and other error handling tools are also supported
\ No newline at end of file diff --git a/src/webassembly/import-js-type.md b/src/webassembly/import-js-type.md new file mode 100644 index 00000000000..9187259d9f6 --- /dev/null +++ b/src/webassembly/import-js-type.md @@ -0,0 +1,89 @@ +# Import user-defined Javascript types + +User-defined Javascript types can be imported by declaring the relevant methods as `extern "C"` just like +other foreign functions. + +For instance, let's declare a class `OutputBox` + +```javascript +import init, {set_panic_hook, edit_box} from '/wasm/project.js'; + +class OutputBox { + constructor(element) { + this.element = element; + this.lastText = null; + } + + setText(text) { + this.element.innerHTML = text; + } + + get currentText() { + return this.element.innerHTML; + } +} + +window.OutputBox = OutputBox; + +(async () => { + // Run the init method to initiate the WebAssembly module. + await init(); + set_panic_hook(); + const wasmoutput = document.querySelector('#wasmoutput'); + const outputBox = new OutputBox(wasmoutput); + const input = document.createElement('input'); + document.body.appendChild(input); + wasmoutput.onclick = () => { + const inputValue = input.value; + edit_box(outputBox, inputValue); + }; +})(); +``` + +It can be imported as such in Rust + +```rust +#[wasm_bindgen] +extern "C" { + pub type OutputBox; + + #[wasm_bindgen(constructor)] + pub fn new(text: i32) -> OutputBox; + + #[wasm_bindgen(method)] + pub fn setText(this: &OutputBox, text: &str); + + // Has to return owned + #[wasm_bindgen(method, getter)] + pub fn lastText(this: &OutputBox) -> Option; + + #[wasm_bindgen(method, setter)] + pub fn set_lastText(this: &OutputBox, text: Option); + + #[wasm_bindgen(method, getter)] + pub fn currentText(this: &OutputBox) -> String; +} + +#[wasm_bindgen] +pub fn edit_box(output_box: &OutputBox, text: &str) { + match text { + "reset" => output_box.set_lastText(None), + "recover" => { + if let Some(last_text) = output_box.lastText() { + output_box.setText(&last_text); + } else { + output_box.setText("No last text"); + } + } + "save" => output_box.set_lastText(Some(output_box.currentText())), + _ => output_box.setText(text), + } +} +``` + +
+ +* Getters and Setters have to be declared with an added parameter in the proc macro. +* `null` and `undefined` can be both represented by `Option::None` + +
\ No newline at end of file diff --git a/src/webassembly/import-method/error-handling.md b/src/webassembly/import-method/error-handling.md new file mode 100644 index 00000000000..8cc9d0a9ecb --- /dev/null +++ b/src/webassembly/import-method/error-handling.md @@ -0,0 +1,18 @@ +# Error handling for imported Javascript methods + +Javascript methods that throw can be imported in Rust like this: + +```javascript +window.throwsIfPair = (num) => { + if (num % 2 === 0) throw new Error("Pair number"); + return num; +}; +``` + +```rust +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(catch)] + pub fn throwsIfPair(text: i32) -> Result; +} +``` diff --git a/src/webassembly/web-sys.md b/src/webassembly/import-method/web-sys.md similarity index 100% rename from src/webassembly/web-sys.md rename to src/webassembly/import-method/web-sys.md From f7d791fac7bfc9f9a4fce43c05137e960cd015d6 Mon Sep 17 00:00:00 2001 From: sakex Date: Mon, 24 Jul 2023 22:10:51 +0200 Subject: [PATCH 12/28] async.md --- src/SUMMARY.md | 2 + .../server/files/index.html | 3 + src/webassembly/async.md | 75 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 src/webassembly/async.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index d531ba2d13d..d3930238eae 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -306,6 +306,8 @@ - [Expose user-defined Rust types](webassembly/expose-rust-type.md) - [Import user-defined Javascript types](webassembly/import-js-type.md) - [Borrow Checker](webassembly/borrow-checker.md) +- [Async](webassembly/async.md) + # Final Words diff --git a/src/rust-wasm-template/server/files/index.html b/src/rust-wasm-template/server/files/index.html index 18e222fd0c4..5be9ee20f5e 100644 --- a/src/rust-wasm-template/server/files/index.html +++ b/src/rust-wasm-template/server/files/index.html @@ -75,6 +75,9 @@ border-radius: 5px; font-weight: bold; cursor: pointer; + display: inline-block; + min-height: 1em; + min-width: 1em; } img { diff --git a/src/webassembly/async.md b/src/webassembly/async.md new file mode 100644 index 00000000000..8eb2faa14ac --- /dev/null +++ b/src/webassembly/async.md @@ -0,0 +1,75 @@ +# Async + +Rust methods in WebAssembly can be declared async. Once called, they will be scheduled on the browser's event loop. +An event handler can for instance be implemented with a tokio channel. + +Instead of `tokio::spawn`, `wasm_bindgen` provides `wasm_bindgen_futures::spawn_local`. + +Let's create a class that waits for messages on a channel to rotate an HTML element: + +```rust +#[derive(Debug)] +enum RotateSide { + Left, + Right, +} + +#[wasm_bindgen] +pub struct Rotator { + sender: Sender, +} + +#[wasm_bindgen] +impl Rotator { + #[wasm_bindgen(constructor)] + pub fn new(element: web_sys::HtmlElement) -> Rotator { + let (sender, mut receiver) = channel::(1); + spawn_local(async move { + let mut rotation = 0; + while let Some(rotate_side) = receiver.recv().await { + log(&format!("Rotation: {}", rotation)); + match rotate_side { + RotateSide::Left => rotation -= 45, + RotateSide::Right => rotation += 45, + } + element.set_inner_html(&rotation.to_string()); + let style = element.style(); + style + .set_property("transform", &format!("rotate({rotation}deg)")) + .expect("Failed to rotate"); + } + }); + Rotator { sender } + } + + #[wasm_bindgen] + pub async fn rotate(&self, msg: String) -> Result<(), JsValue> { + let rotate_side = match msg.as_str() { + "ArrowLeft" => RotateSide::Left, + "ArrowRight" => RotateSide::Right, + _ => return Ok(()), + }; + self.sender + .send(rotate_side) + .await + .map_err(|e| JsValue::from_str(&format!("Receiver dropped {:?}", e))) + } +} +``` + +Let's call it from Javascript + +```javascript +import init, {Rotator} from '/wasm/project.js'; + +(async () => { + // Run the init method to initiate the WebAssembly module. + await init(); + const wasmoutput = document.querySelector('#wasmoutput'); + const rotator = new Rotator(wasmoutput); + document.body.addEventListener('keydown', async (e) => { + await rotator.rotate(e.key); + }); +})(); + +``` From 9b00ca4211264b8bdfa59a61dcbcbb981941dbdc Mon Sep 17 00:00:00 2001 From: sakex Date: Mon, 24 Jul 2023 22:12:47 +0200 Subject: [PATCH 13/28] remove log from async.md --- src/webassembly/async.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/webassembly/async.md b/src/webassembly/async.md index 8eb2faa14ac..a4e8eb26b7e 100644 --- a/src/webassembly/async.md +++ b/src/webassembly/async.md @@ -27,7 +27,6 @@ impl Rotator { spawn_local(async move { let mut rotation = 0; while let Some(rotate_side) = receiver.recv().await { - log(&format!("Rotation: {}", rotation)); match rotate_side { RotateSide::Left => rotation -= 45, RotateSide::Right => rotation += 45, From bee0f950e280ac77a25839ba64d07a00ce51599b Mon Sep 17 00:00:00 2001 From: sakex Date: Tue, 25 Jul 2023 07:49:58 +0000 Subject: [PATCH 14/28] limitations/borrow-checker.md --- src/SUMMARY.md | 3 ++- src/webassembly/{ => limitations}/borrow-checker.md | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename src/webassembly/{ => limitations}/borrow-checker.md (100%) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index d3930238eae..c7320a5794e 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -305,7 +305,8 @@ - [web-sys](webassembly/import-method/web-sys.md) - [Expose user-defined Rust types](webassembly/expose-rust-type.md) - [Import user-defined Javascript types](webassembly/import-js-type.md) -- [Borrow Checker](webassembly/borrow-checker.md) +- [Limitations](webassembly/limitations.md) + - [Borrow Checker](webassembly/limitations/borrow-checker.md) - [Async](webassembly/async.md) diff --git a/src/webassembly/borrow-checker.md b/src/webassembly/limitations/borrow-checker.md similarity index 100% rename from src/webassembly/borrow-checker.md rename to src/webassembly/limitations/borrow-checker.md From a87c286df79fa695271b99544af9604ac626c7c4 Mon Sep 17 00:00:00 2001 From: sakex Date: Tue, 25 Jul 2023 09:30:55 +0000 Subject: [PATCH 15/28] limitations --- src/SUMMARY.md | 1 + src/webassembly/limitations.md | 29 +++++++++++++++++++ src/webassembly/limitations/borrow-checker.md | 5 ++-- src/webassembly/limitations/closures.md | 2 ++ 4 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 src/webassembly/limitations.md create mode 100644 src/webassembly/limitations/closures.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index c7320a5794e..47f49cb58c5 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -307,6 +307,7 @@ - [Import user-defined Javascript types](webassembly/import-js-type.md) - [Limitations](webassembly/limitations.md) - [Borrow Checker](webassembly/limitations/borrow-checker.md) + - [Closures](webassembly/limitations/closures.md) - [Async](webassembly/async.md) diff --git a/src/webassembly/limitations.md b/src/webassembly/limitations.md new file mode 100644 index 00000000000..e54a0ad1e33 --- /dev/null +++ b/src/webassembly/limitations.md @@ -0,0 +1,29 @@ +# Limitations and Workarounds + +Because it runs in a Virtual Machine with its own set of restrictions, +some common native operations are not available in WebAssembly. For instance, +there is no file system in WASM, therefore file operations are handled +through Web APIs. + +Other methods from the standard library that rely on system calls cannot be +used as well. They can however either be replaced by a dependency or be enabled +by setting a feature flag. For instance, + +- Many methods from `std::time` rely on system calls, so they will panic when called +in WASM. [Drop in replacements](https://docs.rs/web-time/latest/web_time/) exist that +use the brower's `Date` and `performance` methods +- `rand` which usually relies on thread local storage, can be made to use +`Math.random()` by setting the dependency feature `getrandom = { ..., features = ["js"] }` +- Statically linking against another language and bundling as one binary is mostly not supported +because the standard WASM ABI is not based on C's like in most environments. Instead, you +should dynamically link with `#[wasm_bindgen] extern "C"`. This means that some crates +which bundle C or C++ binaries will not be portable to WASM. + +Furthermore, interoperability with Javascript, a language that knows nothing about +the borrow-checker but uses Garbage Collection to manage memory forces us to +alter how we use both Rust and Javascript with WebAssembly. + +This chapter covers a few caveats of WASM programming: + +- [Borrow Checker](limitations/borrow-checker.md) +- [Closures](limitations/closures.md) diff --git a/src/webassembly/limitations/borrow-checker.md b/src/webassembly/limitations/borrow-checker.md index b79a932ecfe..b8503706b5c 100644 --- a/src/webassembly/limitations/borrow-checker.md +++ b/src/webassembly/limitations/borrow-checker.md @@ -1,6 +1,7 @@ # Borrow Checker -Types annotated with `wasm_bindgen` can reference each other. +When we export a Rust type to Javascript and the pass an instance of this type to a method that takes ownership of it, the javascript variable will be cleared and dereferencing it will throw a runtime error. +This essentially implements the borrow checker at Runtime in Javascript. ```rust #[wasm_bindgen] @@ -28,8 +29,6 @@ impl MultiCounter { } ``` -But what happens when you call `add_counter` from Javascript ? - ```javascript import init, {set_panic_hook, Counter, MultiCounter} from '/wasm/project.js'; diff --git a/src/webassembly/limitations/closures.md b/src/webassembly/limitations/closures.md new file mode 100644 index 00000000000..1d358843224 --- /dev/null +++ b/src/webassembly/limitations/closures.md @@ -0,0 +1,2 @@ +# Closures + From abfe08c0e7c7030727fd14a78311464af07284d6 Mon Sep 17 00:00:00 2001 From: sakex Date: Wed, 26 Jul 2023 15:21:39 +0000 Subject: [PATCH 16/28] update-deps --- src/rust-wasm-template/project/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rust-wasm-template/project/Cargo.toml b/src/rust-wasm-template/project/Cargo.toml index 23dfe90819c..dc26dc52f2a 100644 --- a/src/rust-wasm-template/project/Cargo.toml +++ b/src/rust-wasm-template/project/Cargo.toml @@ -17,10 +17,12 @@ js-sys = "0.3.51" # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for # code size when deploying. console_error_panic_hook = "0.1.7" +tokio = { version = "1.29.1", features = ["sync"] } [dependencies.web-sys] version = "0.3.4" features = [ + 'CssStyleDeclaration', 'Document', 'Element', 'HtmlElement', From 46c5a440402e25b907371a324355b8574081c749 Mon Sep 17 00:00:00 2001 From: Alexandre Senges Date: Thu, 27 Jul 2023 10:08:49 +0000 Subject: [PATCH 17/28] Fix import in async.md --- src/webassembly/async.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/webassembly/async.md b/src/webassembly/async.md index a4e8eb26b7e..23760aae30e 100644 --- a/src/webassembly/async.md +++ b/src/webassembly/async.md @@ -8,6 +8,10 @@ Instead of `tokio::spawn`, `wasm_bindgen` provides `wasm_bindgen_futures::spawn_ Let's create a class that waits for messages on a channel to rotate an HTML element: ```rust +use wasm_bindgen::prelude::*; +use tokio::sync::mpsc::{Sender, channel}; +use wasm_bindgen_futures::spawn_local; + #[derive(Debug)] enum RotateSide { Left, From 9f724d40cec6de59c66b8347be34a4f61237491d Mon Sep 17 00:00:00 2001 From: sakex Date: Wed, 2 Aug 2023 22:15:50 +0200 Subject: [PATCH 18/28] Use generic server --- Cargo.lock | 1272 ++--------------- Cargo.toml | 3 +- .../{project => }/Cargo.toml | 0 src/rust-wasm-template/README.md | 32 +- src/rust-wasm-template/server/Cargo.toml | 10 - .../server/files/index.html | 106 -- src/rust-wasm-template/server/src/main.rs | 50 - .../{project => }/src/lib.rs | 0 src/rust-wasm-template/static/index.css | 82 ++ src/rust-wasm-template/static/index.html | 24 + .../{server/files => static}/index.mjs | 0 .../files => static}/source-code-regular.otf | Bin src/webassembly/load-wasm-module.md | 34 +- 13 files changed, 280 insertions(+), 1333 deletions(-) rename src/rust-wasm-template/{project => }/Cargo.toml (100%) delete mode 100644 src/rust-wasm-template/server/Cargo.toml delete mode 100644 src/rust-wasm-template/server/files/index.html delete mode 100644 src/rust-wasm-template/server/src/main.rs rename src/rust-wasm-template/{project => }/src/lib.rs (100%) create mode 100644 src/rust-wasm-template/static/index.css create mode 100644 src/rust-wasm-template/static/index.html rename src/rust-wasm-template/{server/files => static}/index.mjs (100%) rename src/rust-wasm-template/{server/files => static}/source-code-regular.otf (100%) diff --git a/Cargo.lock b/Cargo.lock index 92ab3d9e70e..c901b83df55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,284 +3,12 @@ version = 3 [[package]] -name = "actix-codec" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78d1833b3838dbe990df0f1f87baf640cf6146e898166afe401839d1b001e570" -dependencies = [ - "bitflags", - "bytes 0.5.6", - "futures-core", - "futures-sink", - "log", - "pin-project 0.4.30", - "tokio 0.2.25", - "tokio-util 0.3.1", -] - -[[package]] -name = "actix-connect" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177837a10863f15ba8d3ae3ec12fac1099099529ed20083a27fdfe247381d0dc" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "derive_more", - "either", - "futures-util", - "http", - "log", - "trust-dns-proto", - "trust-dns-resolver", -] - -[[package]] -name = "actix-files" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51e8a9146c12fce92a6e4c24b8c4d9b05268130bfd8d61bc587e822c32ce689" -dependencies = [ - "actix-service", - "actix-web", - "bitflags", - "bytes 0.5.6", - "derive_more", - "futures-core", - "futures-util", - "log", - "mime", - "mime_guess", - "percent-encoding", - "v_htmlescape", -] - -[[package]] -name = "actix-http" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be6b66b62a794a8e6d366ac9415bb7d475ffd1e9f4671f38c1d8a8a5df950b3" -dependencies = [ - "actix-codec", - "actix-connect", - "actix-rt", - "actix-service", - "actix-threadpool", - "actix-utils", - "base64 0.13.1", - "bitflags", - "brotli", - "bytes 0.5.6", - "cookie", - "copyless", - "derive_more", - "either", - "encoding_rs", - "flate2", - "futures-channel", - "futures-core", - "futures-util", - "fxhash", - "h2 0.2.7", - "http", - "httparse", - "indexmap", - "itoa 0.4.8", - "language-tags", - "lazy_static", - "log", - "mime", - "percent-encoding", - "pin-project 1.0.12", - "rand 0.7.3", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "sha-1", - "slab", - "time", -] - -[[package]] -name = "actix-macros" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "actix-router" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad299af73649e1fc893e333ccf86f377751eb95ff875d095131574c6f43452c" -dependencies = [ - "bytestring", - "http", - "log", - "regex", - "serde", -] - -[[package]] -name = "actix-rt" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227" -dependencies = [ - "actix-macros", - "actix-threadpool", - "copyless", - "futures-channel", - "futures-util", - "smallvec", - "tokio 0.2.25", -] - -[[package]] -name = "actix-server" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45407e6e672ca24784baa667c5d32ef109ccdd8d5e0b5ebb9ef8a67f4dfb708e" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "futures-channel", - "futures-util", - "log", - "mio 0.6.23", - "mio-uds", - "num_cpus", - "slab", - "socket2 0.3.19", -] - -[[package]] -name = "actix-service" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0052435d581b5be835d11f4eb3bce417c8af18d87ddf8ace99f8e67e595882bb" -dependencies = [ - "futures-util", - "pin-project 0.4.30", -] - -[[package]] -name = "actix-testing" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47239ca38799ab74ee6a8a94d1ce857014b2ac36f242f70f3f75a66f691e791c" -dependencies = [ - "actix-macros", - "actix-rt", - "actix-server", - "actix-service", - "log", - "socket2 0.3.19", -] - -[[package]] -name = "actix-threadpool" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d209f04d002854b9afd3743032a27b066158817965bf5d036824d19ac2cc0e30" -dependencies = [ - "derive_more", - "futures-channel", - "lazy_static", - "log", - "num_cpus", - "parking_lot 0.11.2", - "threadpool", -] - -[[package]] -name = "actix-tls" -version = "2.0.0" +name = "addr2line" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24789b7d7361cf5503a504ebe1c10806896f61e96eca9a7350e23001aca715fb" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" dependencies = [ - "actix-codec", - "actix-service", - "actix-utils", - "futures-util", -] - -[[package]] -name = "actix-utils" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9022dec56632d1d7979e59af14f0597a28a830a9c1c7fec8b2327eb9f16b5a" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "bitflags", - "bytes 0.5.6", - "either", - "futures-channel", - "futures-sink", - "futures-util", - "log", - "pin-project 0.4.30", - "slab", -] - -[[package]] -name = "actix-web" -version = "3.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6534a126df581caf443ba2751cab42092c89b3f1d06a9d829b1e17edfe3e277" -dependencies = [ - "actix-codec", - "actix-http", - "actix-macros", - "actix-router", - "actix-rt", - "actix-server", - "actix-service", - "actix-testing", - "actix-threadpool", - "actix-tls", - "actix-utils", - "actix-web-codegen", - "awc", - "bytes 0.5.6", - "derive_more", - "encoding_rs", - "futures-channel", - "futures-core", - "futures-util", - "fxhash", - "log", - "mime", - "pin-project 1.0.12", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "socket2 0.3.19", - "time", - "tinyvec", - "url", -] - -[[package]] -name = "actix-web-codegen" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad26f77093333e0e7c6ffe54ebe3582d908a104e448723eec6d43d08b07143fb" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "gimli", ] [[package]] @@ -298,21 +26,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - [[package]] name = "allocator-example" version = "0.1.0" @@ -388,17 +101,6 @@ version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" -[[package]] -name = "async-trait" -version = "0.1.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2d0f03b3640e3a630367e40c468cb7f309529c708ed1d88597047b0e7c6ef7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.13", -] - [[package]] name = "atty" version = "0.2.14" @@ -407,7 +109,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi 0.1.19", "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -417,35 +119,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "awc" -version = "2.0.3" +name = "backtrace" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b381e490e7b0cfc37ebc54079b0413d8093ef43d14a4e4747083f7fa47a9e691" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" dependencies = [ - "actix-codec", - "actix-http", - "actix-rt", - "actix-service", - "base64 0.13.1", - "bytes 0.5.6", - "cfg-if 1.0.0", - "derive_more", - "futures-core", - "log", - "mime", - "percent-encoding", - "rand 0.7.3", - "serde", - "serde_json", - "serde_urlencoded", + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", ] -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - [[package]] name = "base64" version = "0.13.1" @@ -464,15 +151,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -482,27 +160,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "brotli" -version = "3.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - [[package]] name = "bstr" version = "1.4.0" @@ -524,15 +181,6 @@ dependencies = [ "spin", ] -[[package]] -name = "buf-min" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa17aa1cf56bdd6bb30518767d00e58019d326f3f05d8c3e0730b549d332ea83" -dependencies = [ - "bytes 0.5.6", -] - [[package]] name = "bumpalo" version = "3.12.0" @@ -545,39 +193,18 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "bytes" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" - [[package]] name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" -[[package]] -name = "bytestring" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238e4886760d98c4f899360c834fa93e62cf7f721ac3c2da375cbdf4b8679aae" -dependencies = [ - "bytes 1.4.0", -] - [[package]] name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -590,7 +217,7 @@ version = "0.1.0" dependencies = [ "futures-util", "http", - "tokio 1.28.1", + "tokio", "tokio-websockets", ] @@ -603,7 +230,7 @@ dependencies = [ "iana-time-zone", "num-integer", "num-traits", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -686,39 +313,16 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen", ] -[[package]] -name = "const_fn" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" - [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "cookie" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" -dependencies = [ - "percent-encoding", - "time", - "version_check 0.9.4", -] - -[[package]] -name = "copyless" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" - [[package]] name = "core-foundation" version = "0.9.3" @@ -744,22 +348,13 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if 1.0.0", -] - [[package]] name = "crossbeam-channel" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", ] @@ -769,7 +364,7 @@ version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -790,7 +385,7 @@ checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" dependencies = [ "cssparser-macros", "dtoa-short", - "itoa 1.0.6", + "itoa", "matches", "phf 0.10.1", "proc-macro2", @@ -862,35 +457,20 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version 0.4.0", + "rustc_version", "syn 1.0.109", ] -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - [[package]] name = "digest" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "crypto-common", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "dtoa" version = "0.4.8" @@ -912,12 +492,6 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591" -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - [[package]] name = "elasticlunr-rs" version = "3.0.2" @@ -936,19 +510,7 @@ version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "enum-as-inner" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "570d109b813e904becc80d8d5da38376818a143348413f7149f1340fe04754d4" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 1.0.109", + "cfg-if", ] [[package]] @@ -1013,22 +575,12 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall 0.2.16", "windows-sys 0.45.0", ] -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1068,22 +620,6 @@ dependencies = [ "libc", ] -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags", - "fuchsia-zircon-sys", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - [[package]] name = "futf" version = "0.1.5" @@ -1094,20 +630,6 @@ dependencies = [ "new_debug_unreachable", ] -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - [[package]] name = "futures-channel" version = "0.3.28" @@ -1159,14 +681,13 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ - "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.9", + "pin-project-lite", "pin-utils", "slab", ] @@ -1187,7 +708,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -1205,7 +726,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -1216,11 +737,17 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "gitignore" version = "1.0.7" @@ -1236,33 +763,13 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "h2" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" -dependencies = [ - "bytes 0.5.6", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio 0.2.25", - "tokio-util 0.3.1", - "tracing", - "tracing-futures", -] - [[package]] name = "h2" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" dependencies = [ - "bytes 1.4.0", + "bytes", "fnv", "futures-core", "futures-sink", @@ -1270,8 +777,8 @@ dependencies = [ "http", "indexmap", "slab", - "tokio 1.28.1", - "tokio-util 0.7.7", + "tokio", + "tokio-util", "tracing", ] @@ -1303,12 +810,12 @@ checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.1", "bitflags", - "bytes 1.4.0", + "bytes", "headers-core", "http", "httpdate", "mime", - "sha1 0.10.5", + "sha1", ] [[package]] @@ -1320,12 +827,6 @@ dependencies = [ "http", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "hermit-abi" version = "0.1.19" @@ -1350,17 +851,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi 0.3.9", -] - [[package]] name = "html5ever" version = "0.26.0" @@ -1381,9 +871,9 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ - "bytes 1.4.0", + "bytes", "fnv", - "itoa 1.0.6", + "itoa", ] [[package]] @@ -1392,9 +882,9 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes 1.4.0", + "bytes", "http", - "pin-project-lite 0.2.9", + "pin-project-lite", ] [[package]] @@ -1430,19 +920,19 @@ version = "0.14.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" dependencies = [ - "bytes 1.4.0", + "bytes", "futures-channel", "futures-core", "futures-util", - "h2 0.3.17", + "h2", "http", "http-body", "httparse", "httpdate", - "itoa 1.0.6", - "pin-project-lite 0.2.9", - "socket2 0.4.9", - "tokio 1.28.1", + "itoa", + "pin-project-lite", + "socket2", + "tokio", "tower-service", "tracing", "want", @@ -1454,10 +944,10 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes 1.4.0", + "bytes", "hyper", "native-tls", - "tokio 1.28.1", + "tokio", "tokio-native-tls", ] @@ -1485,17 +975,6 @@ dependencies = [ "cxx-build", ] -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.3.0" @@ -1542,7 +1021,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1556,27 +1035,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ - "libc", -] - -[[package]] -name = "ipconfig" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" -dependencies = [ - "socket2 0.3.19", - "widestring", - "winapi 0.3.9", - "winreg 0.6.2", -] - [[package]] name = "ipnet" version = "2.7.2" @@ -1595,12 +1053,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.6" @@ -1616,16 +1068,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - [[package]] name = "kqueue" version = "1.0.7" @@ -1646,12 +1088,6 @@ dependencies = [ "libc", ] -[[package]] -name = "language-tags" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" - [[package]] name = "lazy_static" version = "1.4.0" @@ -1660,9 +1096,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "link-cplusplus" @@ -1673,12 +1109,6 @@ dependencies = [ "cc", ] -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.3.1" @@ -1701,16 +1131,7 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", + "cfg-if", ] [[package]] @@ -1739,12 +1160,6 @@ dependencies = [ "tendril", ] -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matches" version = "0.1.10" @@ -1779,7 +1194,7 @@ dependencies = [ "serde_json", "shlex", "tempfile", - "tokio 1.28.1", + "tokio", "toml", "topological-sort", "warp", @@ -1827,25 +1242,6 @@ dependencies = [ "adler", ] -[[package]] -name = "mio" -version = "0.6.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" -dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "libc", - "log", - "miow", - "net2", - "slab", - "winapi 0.2.8", -] - [[package]] name = "mio" version = "0.8.6" @@ -1858,29 +1254,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "mio-uds" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" -dependencies = [ - "iovec", - "libc", - "mio 0.6.23", -] - -[[package]] -name = "miow" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", -] - [[package]] name = "native-tls" version = "0.2.11" @@ -1899,17 +1272,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "net2" -version = "0.2.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", -] - [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -1922,16 +1284,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -[[package]] -name = "nom" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -dependencies = [ - "memchr", - "version_check 0.1.5", -] - [[package]] name = "notify" version = "5.1.0" @@ -1945,7 +1297,7 @@ dependencies = [ "inotify", "kqueue", "libc", - "mio 0.8.6", + "mio", "walkdir", "windows-sys 0.42.0", ] @@ -1989,18 +1341,21 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "opener" version = "0.5.2" @@ -2008,7 +1363,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005" dependencies = [ "bstr", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -2018,7 +1373,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ "bitflags", - "cfg-if 1.0.0", + "cfg-if", "foreign-types", "libc", "once_cell", @@ -2055,17 +1410,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -2073,21 +1417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.7", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if 1.0.0", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi 0.3.9", + "parking_lot_core", ] [[package]] @@ -2096,7 +1426,7 @@ version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall 0.2.16", "smallvec", @@ -2245,33 +1575,13 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pin-project" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" -dependencies = [ - "pin-project-internal 0.4.30", -] - [[package]] name = "pin-project" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" dependencies = [ - "pin-project-internal 1.0.12", -] - -[[package]] -name = "pin-project-internal" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "pin-project-internal", ] [[package]] @@ -2285,12 +1595,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "pin-project-lite" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" - [[package]] name = "pin-project-lite" version = "0.2.9" @@ -2352,6 +1656,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "js-sys", + "tokio", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -2512,11 +1817,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" dependencies = [ "base64 0.21.0", - "bytes 1.4.0", + "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.3.17", + "h2", "http", "http-body", "hyper", @@ -2528,38 +1833,25 @@ dependencies = [ "native-tls", "once_cell", "percent-encoding", - "pin-project-lite 0.2.9", + "pin-project-lite", "serde", "serde_json", "serde_urlencoded", - "tokio 1.28.1", + "tokio", "tokio-native-tls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg 0.10.1", -] - -[[package]] -name = "resolv-conf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" -dependencies = [ - "hostname", - "quick-error", + "winreg", ] [[package]] -name = "rustc_version" -version = "0.2.3" +name = "rustc-demangle" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc_version" @@ -2567,7 +1859,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.17", + "semver", ] [[package]] @@ -2692,27 +1984,12 @@ dependencies = [ "smallvec", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" version = "1.0.159" @@ -2739,7 +2016,7 @@ version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" dependencies = [ - "itoa 1.0.6", + "itoa", "ryu", "serde", ] @@ -2751,19 +2028,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.6", + "itoa", "ryu", "serde", ] -[[package]] -name = "server" -version = "0.1.0" -dependencies = [ - "actix-files", - "actix-web", -] - [[package]] name = "servo_arc" version = "0.2.0" @@ -2774,37 +2043,15 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - [[package]] name = "sha1" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest", ] [[package]] @@ -2819,9 +2066,9 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest", ] [[package]] @@ -2860,17 +2107,6 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" -[[package]] -name = "socket2" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "winapi 0.3.9", -] - [[package]] name = "socket2" version = "0.4.9" @@ -2878,7 +2114,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -2896,64 +2132,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check 0.9.4", -] - -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version 0.2.3", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1 0.6.1", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - [[package]] name = "string_cache" version = "0.8.7" @@ -2962,7 +2140,7 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ "new_debug_unreachable", "once_cell", - "parking_lot 0.12.1", + "parking_lot", "phf_shared 0.10.0", "precomputed-hash", "serde", @@ -3014,7 +2192,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "fastrand", "redox_syscall 0.3.5", "rustix", @@ -3071,53 +2249,6 @@ dependencies = [ "syn 2.0.13", ] -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros", - "version_check 0.9.4", - "winapi 0.3.9", -] - -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn 1.0.109", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -3135,39 +2266,20 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" -dependencies = [ - "bytes 0.5.6", - "futures-core", - "iovec", - "lazy_static", - "libc", - "memchr", - "mio 0.6.23", - "mio-uds", - "pin-project-lite 0.1.12", - "signal-hook-registry", - "slab", - "winapi 0.3.9", -] - -[[package]] -name = "tokio" -version = "1.28.1" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", - "bytes 1.4.0", + "backtrace", + "bytes", "libc", - "mio 0.8.6", + "mio", "num_cpus", - "parking_lot 0.12.1", - "pin-project-lite 0.2.9", + "parking_lot", + "pin-project-lite", "signal-hook-registry", - "socket2 0.4.9", + "socket2", "tokio-macros", "windows-sys 0.48.0", ] @@ -3190,7 +2302,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", - "tokio 1.28.1", + "tokio", ] [[package]] @@ -3200,8 +2312,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" dependencies = [ "futures-core", - "pin-project-lite 0.2.9", - "tokio 1.28.1", + "pin-project-lite", + "tokio", ] [[package]] @@ -3212,35 +2324,21 @@ checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" dependencies = [ "futures-util", "log", - "tokio 1.28.1", + "tokio", "tungstenite", ] -[[package]] -name = "tokio-util" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" -dependencies = [ - "bytes 0.5.6", - "futures-core", - "futures-sink", - "log", - "pin-project-lite 0.1.12", - "tokio 0.2.25", -] - [[package]] name = "tokio-util" version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ - "bytes 1.4.0", + "bytes", "futures-core", "futures-sink", - "pin-project-lite 0.2.9", - "tokio 1.28.1", + "pin-project-lite", + "tokio", "tracing", ] @@ -3251,14 +2349,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d1df20f31f428a351ec353699a82aad49360090e8f6320ac0dacf5def4f1573" dependencies = [ "base64 0.21.0", - "bytes 1.4.0", + "bytes", "fastrand", "futures-util", "http", "httparse", "sha1_smol", - "tokio 1.28.1", - "tokio-util 0.7.7", + "tokio", + "tokio-util", ] [[package]] @@ -3288,9 +2386,9 @@ version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "log", - "pin-project-lite 0.2.9", + "pin-project-lite", "tracing-core", ] @@ -3303,55 +2401,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project 1.0.12", - "tracing", -] - -[[package]] -name = "trust-dns-proto" -version = "0.19.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cad71a0c0d68ab9941d2fb6e82f8fb2e86d9945b94e1661dd0aaea2b88215a9" -dependencies = [ - "async-trait", - "cfg-if 1.0.0", - "enum-as-inner", - "futures", - "idna 0.2.3", - "lazy_static", - "log", - "rand 0.7.3", - "smallvec", - "thiserror", - "tokio 0.2.25", - "url", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.19.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710f593b371175db53a26d0b38ed2978fafb9e9e8d3868b1acd753ea18df0ceb" -dependencies = [ - "cfg-if 0.1.10", - "futures", - "ipconfig", - "lazy_static", - "log", - "lru-cache", - "resolv-conf", - "smallvec", - "thiserror", - "tokio 0.2.25", - "trust-dns-proto", -] - [[package]] name = "try-lock" version = "0.2.4" @@ -3366,12 +2415,12 @@ checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" dependencies = [ "base64 0.13.1", "byteorder", - "bytes 1.4.0", + "bytes", "http", "httparse", "log", "rand 0.8.5", - "sha1 0.10.5", + "sha1", "thiserror", "url", "utf-8", @@ -3395,7 +2444,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check 0.9.4", + "version_check", ] [[package]] @@ -3432,7 +2481,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", - "idna 0.3.0", + "idna", "percent-encoding", ] @@ -3448,50 +2497,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "v_escape" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e0ab5fab1db278a9413d2ea794cb66f471f898c5b020c3c394f6447625d9d4" -dependencies = [ - "buf-min", - "v_escape_derive", -] - -[[package]] -name = "v_escape_derive" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29769400af8b264944b851c961a4a6930e76604f59b1fcd51246bab6a296c8c" -dependencies = [ - "nom", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "v_htmlescape" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f9a8af610ad6f7fc9989c9d2590d9764bc61f294884e9ee93baa58795174572" -dependencies = [ - "cfg-if 1.0.0", - "v_escape", -] - [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - [[package]] name = "version_check" version = "0.9.4" @@ -3524,7 +2535,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27e1a710288f0f91a98dd8a74f05b76a10768db245ce183edf64dc1afdc3016c" dependencies = [ - "bytes 1.4.0", + "bytes", "futures-channel", "futures-util", "headers", @@ -3534,16 +2545,16 @@ dependencies = [ "mime", "mime_guess", "percent-encoding", - "pin-project 1.0.12", + "pin-project", "rustls-pemfile", "scoped-tls", "serde", "serde_json", "serde_urlencoded", - "tokio 1.28.1", + "tokio", "tokio-stream", "tokio-tungstenite", - "tokio-util 0.7.7", + "tokio-util", "tower-service", "tracing", ] @@ -3566,7 +2577,7 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "serde", "serde_json", "wasm-bindgen-macro", @@ -3593,7 +2604,7 @@ version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", @@ -3638,18 +2649,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "widestring" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - [[package]] name = "winapi" version = "0.3.9" @@ -3660,12 +2659,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -3678,7 +2671,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -3843,32 +2836,13 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" -[[package]] -name = "winreg" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "winreg" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -dependencies = [ - "winapi 0.2.8", - "winapi-build", + "winapi", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 44587da6345..57ba757817b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,5 @@ members = [ "src/bare-metal/useful-crates/allocator-example", "src/bare-metal/useful-crates/zerocopy-example", "src/exercises/concurrency/chat-async", - "src/rust-wasm-template/project", - "src/rust-wasm-template/server", + "src/rust-wasm-template", ] diff --git a/src/rust-wasm-template/project/Cargo.toml b/src/rust-wasm-template/Cargo.toml similarity index 100% rename from src/rust-wasm-template/project/Cargo.toml rename to src/rust-wasm-template/Cargo.toml diff --git a/src/rust-wasm-template/README.md b/src/rust-wasm-template/README.md index 885226835f6..91628ad625f 100644 --- a/src/rust-wasm-template/README.md +++ b/src/rust-wasm-template/README.md @@ -2,8 +2,8 @@ This repository contains the minimum amount of code needed to experiment with WebAssembly. Including a web server to serve the HTML and WASM as well as the javascript boilerplate needed to load WASM. -- `/server` contains the Web server as well as the static files -- `/project` contains the Rust code +- `/static` contains the static files including compiled webassembly. +- `/src/lib.rs` contains the Rust code. ## Installation @@ -29,21 +29,35 @@ Alternatively, see the [installation page](https://rustwasm.github.io/wasm-pack/ ## Run the local server +If you have python on your machine, you can simply + ``` -cd server +cd rust-wasm-template/static -cargo run +python3 -m http.server ``` -- On a devcontainer, go to `PORTS` and open the link under `Local Address` for Port `8080` -- Locally, visit http://localhost:8080 +Otherwise a Rust alternative exists and you can install it with -## Build WASM and copy target to the correct path +``` +cargo install cargo server +``` + +And run ``` -cd project +cd rust-wasm-template/static -wasm-pack build --target web && cp -r pkg ../server +cargo-server +``` + +- On a devcontainer, go to `PORTS` and open the link under `Local Address` for Port `8000` +- Locally, visit http://localhost:8000 + +## Build WASM and copy target to the correct path + +``` +wasm-pack build --target web --out-dir static/wasm ``` This command needs to be re-run to view your latest changes. diff --git a/src/rust-wasm-template/server/Cargo.toml b/src/rust-wasm-template/server/Cargo.toml deleted file mode 100644 index a011d84e534..00000000000 --- a/src/rust-wasm-template/server/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "server" -version = "0.1.0" -edition = "2021" -publish = false - -[dependencies] -actix-web = "3.3.2" -actix-files = "0.5.0" - diff --git a/src/rust-wasm-template/server/files/index.html b/src/rust-wasm-template/server/files/index.html deleted file mode 100644 index 5be9ee20f5e..00000000000 --- a/src/rust-wasm-template/server/files/index.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - - WebAssembly Template - - - -

- WebAssembly Template -

-

- This repository contains the minimum amount of code needed to experiment with WebAssembly. Including a web server to serve the HTML and WASM as well as the javascript boilerplate needed to load WASM. -

-

- Edit server/files/index.mjs to edit the Javascript. -

-

-WASM output: -

- - - diff --git a/src/rust-wasm-template/server/src/main.rs b/src/rust-wasm-template/server/src/main.rs deleted file mode 100644 index fffe0528056..00000000000 --- a/src/rust-wasm-template/server/src/main.rs +++ /dev/null @@ -1,50 +0,0 @@ -use actix_files::NamedFile; -use actix_web::{get, web, App, HttpServer, Result}; -use std::path::PathBuf; - -#[get("/")] -async fn index() -> Result { - let path: PathBuf = "./files/index.html".parse()?; - Ok(NamedFile::open(path)?.use_last_modified(true)) -} - -#[get("/files/{name}")] -async fn files(web::Path(name): web::Path) -> Result { - let path: PathBuf = format!("./files/{}", name).parse()?; - Ok(NamedFile::open(path)?.use_last_modified(true)) -} - -#[get("/font.otf")] -async fn font() -> Result { - let path: PathBuf = "./files/source-code-regular.otf".parse()?; - Ok(NamedFile::open(path)?.use_last_modified(true)) -} - -#[get("/wasm/{name}")] -async fn serve_wasm(web::Path(name): web::Path) -> Result { - let path: PathBuf = format!("./pkg/{}", name).parse()?; - Ok(NamedFile::open(path)?.use_last_modified(true)) -} - -#[get("/wasm/snippets/{snippet_name}/{name}")] -async fn serve_wasm_snippet( - web::Path((snippet_name, name)): web::Path<(String, String)>, -) -> Result { - let path: PathBuf = format!("./pkg/snippets/{}/{}", snippet_name, name).parse()?; - Ok(NamedFile::open(path)?.use_last_modified(true)) -} - -#[actix_web::main] -async fn main() -> std::io::Result<()> { - HttpServer::new(|| { - App::new() - .service(index) - .service(files) - .service(serve_wasm) - .service(serve_wasm_snippet) - .service(font) - }) - .bind("127.0.0.1:8080")? - .run() - .await -} diff --git a/src/rust-wasm-template/project/src/lib.rs b/src/rust-wasm-template/src/lib.rs similarity index 100% rename from src/rust-wasm-template/project/src/lib.rs rename to src/rust-wasm-template/src/lib.rs diff --git a/src/rust-wasm-template/static/index.css b/src/rust-wasm-template/static/index.css new file mode 100644 index 00000000000..23e219fc28c --- /dev/null +++ b/src/rust-wasm-template/static/index.css @@ -0,0 +1,82 @@ + +@font-face { + font-family: SourceCodePro; + src: url(/source-code-regular.otf); +} + +body { + background-color: #282c34; + font-family: "SourceCodePro"; +} + +h1 { + margin-left: 40px; + max-width: 530px; + color: #ca6568; +} + +i { + color: #ca6568; +} + +p, ul { + margin-left: 40px; + max-width: 530px; + text-align: justify; + color: #99a0ab; +} + +li { + color: #ca6568; + background-color: #31363f; + margin-bottom: 20px; + max-width: 470px; + padding: 10px; +} + +a { + color: #5fade9; +} + +button { + background-color: #99a0ab; + width: 200px; + padding: 5px 0; + margin-left: calc(50% - 100px - 11px); + border-radius: 5px; + border: 0; + cursor: pointer; + transition: 1s; +} + +button:hover { + background-color: #282c34; + color: white; +} + +input, select { + width: 150px; + padding: 5px; + background-color: #31363f; + border: 1px solid #99a0ab; + color: #99a0ab; + border-radius: 4px; +} + +#wasmoutput { + background-color: aliceblue; + color: #282c34; + padding: 10px; + border-radius: 5px; + font-weight: bold; + cursor: pointer; + display: inline-block; + min-height: 1em; + min-width: 1em; +} + +img { + width: 300px; + height: 300px; + object-fit: contain; +} diff --git a/src/rust-wasm-template/static/index.html b/src/rust-wasm-template/static/index.html new file mode 100644 index 00000000000..904761ce99a --- /dev/null +++ b/src/rust-wasm-template/static/index.html @@ -0,0 +1,24 @@ + + + + + WebAssembly Template + + + + +

+ WebAssembly Template +

+

+ This repository contains the minimum amount of code needed to experiment with WebAssembly. Including a web server to serve the HTML and WASM as well as the javascript boilerplate needed to load WASM. +

+

+ Edit server/files/index.mjs to edit the Javascript. +

+

+WASM output: +

+ + + diff --git a/src/rust-wasm-template/server/files/index.mjs b/src/rust-wasm-template/static/index.mjs similarity index 100% rename from src/rust-wasm-template/server/files/index.mjs rename to src/rust-wasm-template/static/index.mjs diff --git a/src/rust-wasm-template/server/files/source-code-regular.otf b/src/rust-wasm-template/static/source-code-regular.otf similarity index 100% rename from src/rust-wasm-template/server/files/source-code-regular.otf rename to src/rust-wasm-template/static/source-code-regular.otf diff --git a/src/webassembly/load-wasm-module.md b/src/webassembly/load-wasm-module.md index ac2e2502165..67c5abca467 100644 --- a/src/webassembly/load-wasm-module.md +++ b/src/webassembly/load-wasm-module.md @@ -2,24 +2,44 @@ ## Commands to run: + +### Compile Rust lib to WASM + You can compile the basic WASM library provided in [rust-wasm-template](https://github.com/google/comprehensive-rust/tree/main/src/rust-wasm-template) with this command: ```shell -cd src/rust-wasm-template/project +cd src/rust-wasm-template -wasm-pack build --target web && cp -r pkg ../server +wasm-pack build --target web --out-dir static/wasm ``` -You can start the web server provided in [rust-wasm-template](https://github.com/google/comprehensive-rust/tree/main/src/rust-wasm-template) with this command: +### Serve static files +Webassembly has to be loaded from an HTTP server in order to be loaded on the brower. -```shell -cd src/rust-wasm-template/server +If you have python on your machine, you can simply + +``` +cd rust-wasm-template/static + +python3 -m http.server +``` + +Otherwise a Rust alternative exists and you can install it with + +``` +cargo install cargo server +``` + +And run + +``` +cd rust-wasm-template/static -cargo run +cargo-server ``` -Open the web page on port `8080`. HTML and JS files are provided at [rust-wasm-template/server](https://github.com/google/comprehensive-rust/tree/main/src/rust-wasm-template/server/files). +Open the web page on port `8000`. HTML and JS files are provided at [rust-wasm-template/static](https://github.com/google/comprehensive-rust/tree/main/src/rust-wasm-template/static). ## Javascript From afbd29e814ae4e00b1ecec99926c411e6d47bc5e Mon Sep 17 00:00:00 2001 From: sakex Date: Wed, 2 Aug 2023 23:08:12 +0200 Subject: [PATCH 19/28] closures.md --- src/webassembly/limitations/closures.md | 51 +++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/webassembly/limitations/closures.md b/src/webassembly/limitations/closures.md index 1d358843224..83642e9b97b 100644 --- a/src/webassembly/limitations/closures.md +++ b/src/webassembly/limitations/closures.md @@ -1,2 +1,53 @@ # Closures +Closures can be returned to Rust and executed on the WASM runtime. + +```rust +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + fn setInterval(closure: &Closure, millis: u32) -> f64; +} + +#[wasm_bindgen] +pub struct ClosureHandle { + closure: Closure, +} + +#[wasm_bindgen] +pub fn timeout_set_seconds(elem: web_sys::HtmlElement) -> ClosureHandle { + let seconds = Rc::new(RefCell::new(0usize)); + elem.set_inner_html("0 seconds"); + let closure = Closure::wrap(Box::new(move || { + let mut seconds = seconds.borrow_mut(); + *seconds += 1; + elem.set_inner_html(&format!("{} seconds", seconds)); + }) as Box); + setInterval(&closure, 1000); + ClosureHandle { closure } +} +``` + +```javascript +import init, {set_panic_hook, timeout_set_seconds} from '/wasm/project.js'; + +(async () => { + // Run the init method to initiate the WebAssembly module. + await init(); + const wasmOutput = document.querySelector("#wasmoutput"); + timeout_set_seconds(wasmOutput); +})(); + +``` + +
+ +* Since the function that creates the closure keeps its ownership, the closure would be dropped if we did't return it. + * Returning ownership allows the JS runtime to manage the lifetime of the closure and to collect it when it can. + * Try returning nothing from the method. +* Closures can only be passed by reference to WASM functions. + * This is why we pass `&Closure` to `setInterval`. + * This is also why we need to create `ClosureHandle` to return the closure. + +
\ No newline at end of file From 449ac4d30299a973e8c198b33e0e3de0e095bb7f Mon Sep 17 00:00:00 2001 From: sakex Date: Thu, 3 Aug 2023 03:42:40 +0200 Subject: [PATCH 20/28] camera.md --- src/SUMMARY.md | 3 +- src/exercises/webassembly/camera.md | 48 +++++++++++ src/exercises/webassembly/solutions-camera.md | 85 +++++++++++++++++++ src/exercises/webassembly/webassembly.md | 1 + src/rust-wasm-template/Cargo.toml | 1 + .../static/exercises/camera/index.html | 30 +++++++ .../static/exercises/camera/index.mjs | 38 +++++++++ 7 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 src/exercises/webassembly/camera.md create mode 100644 src/exercises/webassembly/solutions-camera.md create mode 100644 src/exercises/webassembly/webassembly.md create mode 100644 src/rust-wasm-template/static/exercises/camera/index.html create mode 100644 src/rust-wasm-template/static/exercises/camera/index.mjs diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 47f49cb58c5..5018c56e986 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -309,7 +309,8 @@ - [Borrow Checker](webassembly/limitations/borrow-checker.md) - [Closures](webassembly/limitations/closures.md) - [Async](webassembly/async.md) - +- [Exercises](exercises/webassembly/webassembly.md) + - [Camera](exercises/webassembly/camera.md) # Final Words diff --git a/src/exercises/webassembly/camera.md b/src/exercises/webassembly/camera.md new file mode 100644 index 00000000000..4d4a89114ed --- /dev/null +++ b/src/exercises/webassembly/camera.md @@ -0,0 +1,48 @@ +# Camera + +In this exercise we will play with the camera in real time. + +Serve the web server and navigate to [http://localhost:8000/exercises/camera/](http://localhost:8000/exercises/camera/). + +You will edit [lib.rs](../../rust-wasm-template/lib.rs) as usual. + +Here is the basic code you will need: + +```rust +extern crate console_error_panic_hook; + +use js_sys::Uint8ClampedArray; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + fn alert(s: &str); + + #[wasm_bindgen(js_namespace = console)] + pub fn log(s: &str); +} + +#[wasm_bindgen] +pub fn set_panic_hook() { + console_error_panic_hook::set_once(); +} + +#[wasm_bindgen] +pub fn edit_bitmap(image_data: &Uint8ClampedArray, width: u32, height: u32) { +} + +``` + +We want to edit the function `edit_bitmap`. `image_data` is a reference to the [Uint8ClampedArray](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Uint8ClampedArray.html) being rendered on your screen. `Uint8ClampedArray` is a flat array of unsigned int of `8` bits. +It represents `srgb` image, which means that each pixel is represented as a vector of 4 elements [red, green, blue, illumination]. +The image can be thought of as a matrix of dimension `(width, height, 4)`, it is row-major. + +We will reuse our different implementations. So do not erase them when going from one exercise to the next. + +1. The first exercise is to turn the top half the image to black. +2. Turn the left half of the image to white. +3. Create an X-ray effect. This is done by mapping colors to their opposite, for instance `0<->255`, `100<->155`. Beware of illumination. +4. You can play with the function, for instance you can implement greyscale, or add random noise. +5. In the `setup` function create a dropdown (``) that will change which transformation to apply to the image. +```rust +static METHODS: [&str; 3] = ["xray", "top-black", "left-white"]; + +#[wasm_bindgen] +pub fn setup() -> Result<(), JsValue> { + console_error_panic_hook::set_once(); + let window = web_sys::window().expect("no global `window` exists"); + let document = window.document().expect("should have a document on window"); + let body = document.body().expect("document should have a body"); + + let select = document.create_element("select")?; + for option_value in METHODS { + let option = document.create_element("option")?; + option.set_inner_html(option_value); + option.set_attribute("value", option_value)?; + select.append_child(&option)?; + } + body.append_child(&select)?; + Ok(()) +} + +#[wasm_bindgen] +pub fn edit_bitmap( + image_data: &Uint8ClampedArray, + width: u32, + height: u32, +) -> Result<(), JsValue> { + let window = web_sys::window().expect("no global `window` exists"); + let document = window.document().expect("should have a document on window"); + let select = document + .query_selector("select")? + .expect("No select") + .dyn_into::() + .expect("Failed to cast `) that will change which transformation to apply to the image. -6. Blur only the background. This can be done by figuring out only the pixels that didn't change between multiple frames. +6. (BONUS) Track moving objects. This can be done by figuring out only the pixels that didn't change between multiple frames. For instance, you could compute the standard deviation of the pixel and black out below a threshold. While this can be achieved without touching Javascript, I recommend editing it. \ No newline at end of file diff --git a/src/exercises/webassembly/solutions-camera.md b/src/exercises/webassembly/solutions-camera.md index 139079774b0..3b5bfbe4671 100644 --- a/src/exercises/webassembly/solutions-camera.md +++ b/src/exercises/webassembly/solutions-camera.md @@ -82,4 +82,89 @@ pub fn edit_bitmap( }; Ok(()) } +``` + +6. Track moving objects. This can be done by figuring out only the pixels that didn't change between multiple frames. For instance, you could compute the standard deviation of the pixel and black out below a threshold. +While this can be achieved without touching Javascript, I recommend editing it. +```rust +extern crate console_error_panic_hook; + +use js_sys::Uint8ClampedArray; +use std::collections::VecDeque; +use tokio::sync::mpsc::{channel, Receiver, Sender}; +use wasm_bindgen::{prelude::*, Clamped}; +use wasm_bindgen_futures::spawn_local; +use web_sys::ImageData; + +#[wasm_bindgen] +extern "C" { + fn alert(s: &str); + + #[wasm_bindgen(js_namespace = console)] + pub fn log(s: &str); +} + +#[wasm_bindgen] +pub struct FrameSender { + sdr: Sender, +} + +#[wasm_bindgen] +impl FrameSender { + pub fn send(&self, data: Uint8ClampedArray) -> Result<(), JsValue> { + self.sdr.try_send(data).map_err(|e| e.to_string().into()) + } +} + +async fn frame_processor( + mut image_data: Receiver, + canvas_ctx: web_sys::CanvasRenderingContext2d, +) { + let mut deque: VecDeque> = VecDeque::new(); + let mut current_data; + while let Some(image_data) = image_data.recv().await { + deque.push_back(image_data.to_vec()); + if deque.len() < 10 { + continue; + } + let first = deque.pop_front().unwrap(); + for (idx, v) in first.into_iter().enumerate() { + let pixels: Vec = deque + .iter() + .map(|image| image[idx] as f32) + .chain(Some(v as f32)) + .collect(); + let mean = pixels.iter().fold(0.0, |a, b| a + *b) / pixels.len() as f32; + let variance = pixels.iter().fold(0.0, |a, b| a + (b - mean).powi(2)) + / pixels.len() as f32; + if variance.sqrt() < 8.0 && idx % 4 != 3 { + image_data.set_index(idx as u32, 0); + } + } + current_data = image_data.to_vec(); + canvas_ctx + .put_image_data( + &ImageData::new_with_u8_clamped_array(Clamped(¤t_data), 400) + .unwrap(), + 0.0, + 0.0, + ) + .unwrap(); + } +} + +#[wasm_bindgen] +pub fn setup( + canvas_ctx: web_sys::CanvasRenderingContext2d, +) -> Result { + console_error_panic_hook::set_once(); + + let (sdr, rcv) = channel(2); + let sender = FrameSender { sdr }; + spawn_local(async move { + frame_processor(rcv, canvas_ctx).await; + }); + Ok(sender) +} + ``` \ No newline at end of file diff --git a/src/rust-wasm-template/Cargo.toml b/src/rust-wasm-template/Cargo.toml index f7e1800f1c5..0e02bc3dd6c 100644 --- a/src/rust-wasm-template/Cargo.toml +++ b/src/rust-wasm-template/Cargo.toml @@ -25,6 +25,8 @@ features = [ 'CssStyleDeclaration', 'Document', 'Element', + 'ImageData', + 'CanvasRenderingContext2d', 'HtmlSelectElement', 'HtmlElement', 'Node', From 771e41ccddbc1f5f88b7df79ba7206d02025a087 Mon Sep 17 00:00:00 2001 From: sakex Date: Sun, 6 Aug 2023 19:09:13 +0200 Subject: [PATCH 22/28] Clearer instructions in camera.md --- src/exercises/webassembly/camera.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/exercises/webassembly/camera.md b/src/exercises/webassembly/camera.md index c36c5534dab..39fe445dea4 100644 --- a/src/exercises/webassembly/camera.md +++ b/src/exercises/webassembly/camera.md @@ -39,10 +39,12 @@ The image can be thought of as a matrix of dimension `(width, height, 4)`, it is We will reuse our different implementations. So do not erase them when going from one exercise to the next. -1. The first exercise is to turn the top half the image to black. -2. Turn the left half of the image to white. +First off let's implement some methods that modify the video live: + +1. Paint the top half the image to black. +2. Paint the left half of the image to white. 3. Create an X-ray effect. This is done by mapping colors to their opposite, for instance `0<->255`, `100<->155`. Beware of illumination. -4. You can play with the function, for instance you can implement greyscale, or add random noise. -5. In the `setup` function create a dropdown (``) that will change which transformation to apply to the image. +6. `(BONUS)` Track moving objects. This can be done by figuring out only the pixels that didn't change between multiple frames. For instance, you could compute the standard deviation of the pixel and black out below a threshold. While this can be achieved without touching Javascript, I recommend editing it. \ No newline at end of file From 6f94c9e7a494fcaca2c58a9892f2f6d72299b329 Mon Sep 17 00:00:00 2001 From: sakex Date: Sun, 6 Aug 2023 23:26:15 +0200 Subject: [PATCH 23/28] game-of-life.md --- src/exercises/webassembly/camera.md | 4 +- src/exercises/webassembly/game-of-life.md | 46 +++++ .../webassembly/solutions-game-of-life.md | 190 ++++++++++++++++++ src/rust-wasm-template/Cargo.toml | 1 + .../static/exercises/game_of_life/index.html | 25 +++ src/webassembly/import-method/web-sys.md | 6 +- 6 files changed, 267 insertions(+), 5 deletions(-) create mode 100644 src/exercises/webassembly/game-of-life.md create mode 100644 src/exercises/webassembly/solutions-game-of-life.md create mode 100644 src/rust-wasm-template/static/exercises/game_of_life/index.html diff --git a/src/exercises/webassembly/camera.md b/src/exercises/webassembly/camera.md index 39fe445dea4..c386b6b260e 100644 --- a/src/exercises/webassembly/camera.md +++ b/src/exercises/webassembly/camera.md @@ -23,7 +23,7 @@ extern "C" { } #[wasm_bindgen] -pub fn set_panic_hook() { +pub fn setup() { console_error_panic_hook::set_once(); } @@ -47,4 +47,4 @@ First off let's implement some methods that modify the video live: 4. `(BONUS)` Now feel free to implement other transformations such as `greyscale` or adding `random noise`. 5. Let's now add functionalities to our page. In the `setup` function create a dropdown (``) that will change which transformation to apply to the image. -6. `(BONUS)` Track moving objects. This can be done by figuring out only the pixels that didn't change between multiple frames. For instance, you could compute the standard deviation of the pixel and black out below a threshold. +4. _Bonus question:_ Now feel free to implement other transformations such as _greyscale_ or adding _random noise_. +5. Let's now add functionalities to our page. In the _setup_ function create a dropdown (`