From 570c194e82983e9eb7678ae82fe30834e49fe66e Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sun, 16 Apr 2023 22:57:12 -0400 Subject: [PATCH 01/12] Macros for frontend nRPC service generation --- Cargo.lock | 896 ++++++++++++++++----- Cargo.toml | 30 +- usdpl-back/Cargo.toml | 18 +- usdpl-back/build.rs | 3 + usdpl-back/src/api_common/dirs.rs | 6 - usdpl-back/src/api_crankshaft/mod.rs | 1 - usdpl-back/src/rpc/mod.rs | 0 usdpl-build/Cargo.toml | 18 + usdpl-build/protos/debug.proto | 25 + usdpl-build/protos/translations.proto | 19 + usdpl-build/src/back/mod.rs | 7 + usdpl-build/src/front/mod.rs | 22 + usdpl-build/src/front/preprocessor.rs | 27 + usdpl-build/src/front/service_generator.rs | 592 ++++++++++++++ usdpl-build/src/front/shared_state.rs | 26 + usdpl-build/src/lib.rs | 5 + usdpl-build/src/proto_files.rs | 44 + usdpl-core/Cargo.toml | 7 +- usdpl-core/src/api_common/target.rs | 7 - usdpl-core/src/api_crankshaft/mod.rs | 1 - usdpl-core/src/socket.rs | 8 - usdpl-front/Cargo.toml | 19 +- usdpl-front/build.rs | 3 + usdpl-front/src/client_handler.rs | 81 ++ usdpl-front/src/lib.rs | 9 +- 25 files changed, 1612 insertions(+), 262 deletions(-) create mode 100644 usdpl-back/build.rs delete mode 100644 usdpl-back/src/api_crankshaft/mod.rs create mode 100644 usdpl-back/src/rpc/mod.rs create mode 100644 usdpl-build/Cargo.toml create mode 100644 usdpl-build/protos/debug.proto create mode 100644 usdpl-build/protos/translations.proto create mode 100644 usdpl-build/src/back/mod.rs create mode 100644 usdpl-build/src/front/mod.rs create mode 100644 usdpl-build/src/front/preprocessor.rs create mode 100644 usdpl-build/src/front/service_generator.rs create mode 100644 usdpl-build/src/front/shared_state.rs create mode 100644 usdpl-build/src/lib.rs create mode 100644 usdpl-build/src/proto_files.rs delete mode 100644 usdpl-core/src/api_crankshaft/mod.rs create mode 100644 usdpl-front/build.rs create mode 100644 usdpl-front/src/client_handler.rs diff --git a/Cargo.lock b/Cargo.lock index 340a0bd..8af7fe1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,25 +39,42 @@ dependencies = [ ] [[package]] -name = "async-recursion" -version = "1.0.2" +name = "anyhow" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" + +[[package]] +name = "async-channel" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-recursion" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "async-trait" -version = "0.1.64" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -72,6 +89,18 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + [[package]] name = "bitflags" version = "1.3.2" @@ -80,23 +109,13 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] -[[package]] -name = "buf_redux" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" -dependencies = [ - "memchr", - "safemem", -] - [[package]] name = "bumpalo" version = "3.12.0" @@ -115,6 +134,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + [[package]] name = "cfg-if" version = "1.0.0" @@ -130,6 +155,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "concurrent-queue" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -142,13 +176,22 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" dependencies = [ "libc", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -178,6 +221,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "encoding" version = "0.2.33" @@ -242,6 +291,33 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "fastrand" version = "1.9.0" @@ -251,6 +327,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "fnv" version = "1.0.7" @@ -267,10 +349,25 @@ dependencies = [ ] [[package]] -name = "futures-channel" -version = "0.3.26" +name = "futures" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -278,31 +375,63 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -310,9 +439,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -320,9 +449,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", @@ -340,10 +469,43 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.3.15" +name = "gloo-net" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "h2" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" dependencies = [ "bytes", "fnv", @@ -370,7 +532,7 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ - "base64", + "base64 0.13.1", "bitflags", "bytes", "headers-core", @@ -389,6 +551,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.2.6" @@ -398,6 +566,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -446,9 +620,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -480,9 +654,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -498,10 +672,30 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.5" +name = "io-lifetimes" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" @@ -513,10 +707,22 @@ dependencies = [ ] [[package]] -name = "libc" -version = "0.2.139" +name = "lazy_static" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" + +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" [[package]] name = "log" @@ -527,6 +733,38 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "logos" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" +dependencies = [ + "beef", + "fnv", + "proc-macro2", + "quote", + "regex-syntax", + "syn 2.0.15", +] + +[[package]] +name = "logos-derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" +dependencies = [ + "logos-codegen", +] + [[package]] name = "memchr" version = "2.5.0" @@ -534,10 +772,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "mime" -version = "0.3.16" +name = "miette" +version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "7abdc09c381c9336b9f2e9bd6067a9a5290d20e2d2e2296f275456121c33ae89" +dependencies = [ + "miette-derive", + "once_cell", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "5.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8842972f23939443013dfd3720f46772b743e86f1a81d120d4b6fb090f87de1c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" @@ -562,21 +823,50 @@ dependencies = [ ] [[package]] -name = "multipart" -version = "0.18.0" +name = "multimap" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "multiparty" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed1ec6589a6d4a1e0b33b4c0a3f6ee96dfba88ebdb3da51403fd7cf0a24a4b04" dependencies = [ - "buf_redux", + "bytes", + "futures-core", "httparse", - "log", - "mime", - "mime_guess", - "quick-error", - "rand", - "safemem", - "tempfile", - "twoway", + "memchr", + "pin-project-lite", + "try-lock", +] + +[[package]] +name = "nrpc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8770a9a12e53035a536e41b7899d72bc423b2b8c17402c8f0dbe44d6bb255ac6" +dependencies = [ + "async-trait", + "bytes", + "prost", +] + +[[package]] +name = "nrpc-build" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5914c2bfd187a4bdfa7a69330e1ef4d8dea4060d8a08bf146f622aa7f176680" +dependencies = [ + "nrpc", + "prettyplease 0.2.4", + "proc-macro2", + "prost-build", + "prost-types", + "protox", + "quote", + "syn 2.0.15", ] [[package]] @@ -585,7 +875,7 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] @@ -613,6 +903,16 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "pin-project" version = "1.0.12" @@ -630,7 +930,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -664,25 +964,133 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] -name = "proc-macro2" -version = "1.0.51" +name = "prettyplease" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "prettyplease" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" +dependencies = [ + "proc-macro2", + "syn 2.0.15", +] + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] -name = "quick-error" -version = "1.2.3" +name = "prost" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +dependencies = [ + "bytes", + "heck", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prettyplease 0.1.25", + "prost", + "prost-types", + "regex", + "syn 1.0.109", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-reflect" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4df26814fb2cd59b1764305047aac8e9c467efe84a9e51bfd0c8811f08fe88a" +dependencies = [ + "logos", + "miette", + "once_cell", + "prost", + "prost-types", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost", +] + +[[package]] +name = "protox" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cb2d1d74977bfa8fc4ddac1951ea639197c2896a830b6c5640085e498726da2" +dependencies = [ + "bytes", + "miette", + "prost", + "prost-reflect", + "prost-types", + "protox-parse", + "thiserror", +] + +[[package]] +name = "protox-parse" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df78235a8c28dc4c5d8acb498d3000851b61b80a68774ff7b43323371b082ea4" +dependencies = [ + "logos", + "miette", + "prost-types", + "thiserror", +] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -719,42 +1127,56 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ - "winapi", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "rustix" +version = "0.37.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", ] [[package]] name = "rustls-pemfile" -version = "0.2.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64", + "base64 0.21.0", ] [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" - -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "scoped-tls" @@ -764,15 +1186,15 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -791,17 +1213,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha-1" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "sha1" version = "0.10.5" @@ -815,18 +1226,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -840,9 +1251,20 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -851,36 +1273,35 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys 0.45.0", ] [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -900,26 +1321,25 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.25.0" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "pin-project-lite", "socket2", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" dependencies = [ "futures-core", "pin-project-lite", @@ -928,9 +1348,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.17.2" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" dependencies = [ "futures-util", "log", @@ -987,32 +1407,23 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "tungstenite" -version = "0.17.3" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" dependencies = [ - "base64", + "base64 0.13.1", "byteorder", "bytes", "http", "httparse", "log", "rand", - "sha-1", + "sha1", "thiserror", "url", "utf-8", ] -[[package]] -name = "twoway" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" -dependencies = [ - "memchr", -] - [[package]] name = "typenum" version = "1.16.0" @@ -1030,15 +1441,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -1049,6 +1460,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + [[package]] name = "universal-hash" version = "0.4.1" @@ -1070,13 +1487,9 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "usdpl" -version = "0.10.0" - [[package]] name = "usdpl-back" -version = "0.10.1" +version = "0.11.0" dependencies = [ "async-recursion", "async-trait", @@ -1084,29 +1497,50 @@ dependencies = [ "gettext-ng", "hex", "log", + "nrpc", "obfstr", "tokio", + "usdpl-build", "usdpl-core", "warp", ] +[[package]] +name = "usdpl-build" +version = "0.11.0" +dependencies = [ + "nrpc-build", + "prettyplease 0.2.4", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn 2.0.15", +] + [[package]] name = "usdpl-core" -version = "0.10.0" +version = "0.11.0" dependencies = [ "aes-gcm-siv", - "base64", + "base64 0.13.1", "hex-literal", ] [[package]] name = "usdpl-front" -version = "0.10.1" +version = "0.11.0" dependencies = [ + "async-channel", "console_error_panic_hook", + "futures", + "gloo-net", "hex", "js-sys", + "nrpc", "obfstr", + "prost", + "usdpl-build", "usdpl-core", "wasm-bindgen", "wasm-bindgen-futures", @@ -1138,9 +1572,9 @@ dependencies = [ [[package]] name = "warp" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7b8be92646fc3d18b06147664ebc5f48d222686cb11a8755e561a735aacc6d" +checksum = "27e1a710288f0f91a98dd8a74f05b76a10768db245ce183edf64dc1afdc3016c" dependencies = [ "bytes", "futures-channel", @@ -1151,7 +1585,7 @@ dependencies = [ "log", "mime", "mime_guess", - "multipart", + "multiparty", "percent-encoding", "pin-project", "rustls-pemfile", @@ -1194,7 +1628,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -1228,7 +1662,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1273,6 +1707,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1295,86 +1740,137 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - [[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "zeroize" diff --git a/Cargo.toml b/Cargo.toml index 7d15700..4c8660f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,14 @@ -[package] -name = "usdpl" -version = "0.10.0" -authors = ["NGnius (Graham) "] -edition = "2021" -license = "GPL-3.0-only" -repository = "https://github.com/NGnius/usdpl-rs" -readme = "README.md" +[workspace] +members = [ + "usdpl-core", + "usdpl-front", + "usdpl-back", + "usdpl-build", +] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +exclude = [ + "templates/decky/backend" +] [profile.release] # Tell `rustc` to optimize for small code size. @@ -16,14 +17,3 @@ debug = false strip = true lto = true codegen-units = 4 - -[workspace] -members = [ - "usdpl-core", - "usdpl-front", - "usdpl-back", -] - -exclude = [ - "templates/decky/backend" -] diff --git a/usdpl-back/Cargo.toml b/usdpl-back/Cargo.toml index 69807b4..4941d35 100644 --- a/usdpl-back/Cargo.toml +++ b/usdpl-back/Cargo.toml @@ -1,25 +1,26 @@ [package] name = "usdpl-back" -version = "0.10.1" +version = "0.11.0" edition = "2021" license = "GPL-3.0-only" repository = "https://github.com/NGnius/usdpl-rs" -readme = "README.md" +readme = "../README.md" description = "Universal Steam Deck Plugin Library back-end" [features] -default = ["blocking", "translate"] +default = ["blocking"] decky = ["usdpl-core/decky"] -crankshaft = ["usdpl-core/crankshaft"] blocking = ["tokio", "tokio/rt", "tokio/rt-multi-thread"] # synchronous API for async functionality, using tokio encrypt = ["usdpl-core/encrypt", "obfstr", "hex"] -translate = ["usdpl-core/translate", "gettext-ng"] [dependencies] -usdpl-core = { version = "0.10", path = "../usdpl-core"} +usdpl-core = { version = "0.11", path = "../usdpl-core"} log = "0.4" +# gRPC/protobuf +nrpc = "0.2" + # HTTP web framework warp = { version = "0.3" } bytes = { version = "1.1" } @@ -34,4 +35,7 @@ obfstr = { version = "0.3", optional = true } hex = { version = "0.4", optional = true } # translations -gettext-ng = { version = "0.4.1", optional = true } +gettext-ng = { version = "0.4.1" } + +[build-dependencies] +usdpl-build = { version = "0.11", path = "../usdpl-build" } diff --git a/usdpl-back/build.rs b/usdpl-back/build.rs new file mode 100644 index 0000000..36803b5 --- /dev/null +++ b/usdpl-back/build.rs @@ -0,0 +1,3 @@ +fn main() { + usdpl_build::back::build() +} diff --git a/usdpl-back/src/api_common/dirs.rs b/usdpl-back/src/api_common/dirs.rs index b07f347..033aee6 100644 --- a/usdpl-back/src/api_common/dirs.rs +++ b/usdpl-back/src/api_common/dirs.rs @@ -6,8 +6,6 @@ use std::path::PathBuf; pub fn home() -> Option { #[cfg(not(any(feature = "decky", feature = "crankshaft")))] let result = crate::api_any::dirs::home(); - #[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))] - let result = None; // TODO #[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))] let result = crate::api_decky::home().ok() .map(|x| PathBuf::from(x) @@ -23,8 +21,6 @@ pub fn home() -> Option { pub fn plugin() -> Option { #[cfg(not(any(feature = "decky", feature = "crankshaft")))] let result = None; // TODO - #[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))] - let result = None; // TODO #[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))] let result = crate::api_decky::plugin_dir().ok().map(|x| x.into()); @@ -35,8 +31,6 @@ pub fn plugin() -> Option { pub fn log() -> Option { #[cfg(not(any(feature = "decky", feature = "crankshaft")))] let result = crate::api_any::dirs::log(); - #[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))] - let result = None; // TODO #[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))] let result = crate::api_decky::log_dir().ok().map(|x| x.into()); diff --git a/usdpl-back/src/api_crankshaft/mod.rs b/usdpl-back/src/api_crankshaft/mod.rs deleted file mode 100644 index c040648..0000000 --- a/usdpl-back/src/api_crankshaft/mod.rs +++ /dev/null @@ -1 +0,0 @@ -compile_error!("Crankshaft unsupported (project no longer maintained)"); diff --git a/usdpl-back/src/rpc/mod.rs b/usdpl-back/src/rpc/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/usdpl-build/Cargo.toml b/usdpl-build/Cargo.toml new file mode 100644 index 0000000..f2f4ab7 --- /dev/null +++ b/usdpl-build/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "usdpl-build" +version = "0.11.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nrpc-build = "0.5" +prost-build = "0.11" +prost-types = "0.11" + +# code gen +prettyplease = "0.2" +quote = "1.0" +syn = "2.0" +proc-macro2 = "1.0" + diff --git a/usdpl-build/protos/debug.proto b/usdpl-build/protos/debug.proto new file mode 100644 index 0000000..c5b7168 --- /dev/null +++ b/usdpl-build/protos/debug.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package usdpl; + +// The translation service +service DevTools { + // Retrieves all translations for the provided 4-letter code + rpc Log (LogMessage) returns (Empty); +} + +enum LogLevel { + Trace = 0; + Debug = 1; + Info = 2; + Warn = 3; + Error = 4; +} + +// The request message containing the log message +message LogMessage { + LogLevel level = 1; + string msg = 2; +} + +message Empty {} diff --git a/usdpl-build/protos/translations.proto b/usdpl-build/protos/translations.proto new file mode 100644 index 0000000..3fc4e59 --- /dev/null +++ b/usdpl-build/protos/translations.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package usdpl; + +// The translation service +service Translations { + // Retrieves all translations for the provided 4-letter code + rpc GetLanguage (LanguageRequest) returns (TranslationsReply) {} +} + +// The request message containing the language code +message LanguageRequest { + string lang = 1; +} + +// The response message containing all translations for the language +message TranslationsReply { + map translations = 1; +} diff --git a/usdpl-build/src/back/mod.rs b/usdpl-build/src/back/mod.rs new file mode 100644 index 0000000..65d0120 --- /dev/null +++ b/usdpl-build/src/back/mod.rs @@ -0,0 +1,7 @@ +pub fn build() { + crate::dump_protos_out().unwrap(); + nrpc_build::compile_servers( + crate::all_proto_filenames().map(|n| crate::proto_out_path().clone().join(n)), + [crate::proto_out_path()] + ) +} diff --git a/usdpl-build/src/front/mod.rs b/usdpl-build/src/front/mod.rs new file mode 100644 index 0000000..698aa9d --- /dev/null +++ b/usdpl-build/src/front/mod.rs @@ -0,0 +1,22 @@ +mod preprocessor; +pub use preprocessor::WasmProtoPreprocessor; + +mod service_generator; +pub use service_generator::WasmServiceGenerator; + +mod shared_state; +pub(crate) use shared_state::SharedState; + +pub fn build() { + let shared_state = SharedState::new(); + crate::dump_protos_out().unwrap(); + nrpc_build::Transpiler::new( + crate::all_proto_filenames().map(|n| crate::proto_out_path().clone().join(n)), + [crate::proto_out_path()] + ).unwrap() + .generate_client() + .with_preprocessor(nrpc_build::AbstractImpl::outer(WasmProtoPreprocessor::with_state(&shared_state))) + .with_service_generator(nrpc_build::AbstractImpl::outer(WasmServiceGenerator::with_state(&shared_state))) + .transpile() + .unwrap() +} diff --git a/usdpl-build/src/front/preprocessor.rs b/usdpl-build/src/front/preprocessor.rs new file mode 100644 index 0000000..dd3b4a2 --- /dev/null +++ b/usdpl-build/src/front/preprocessor.rs @@ -0,0 +1,27 @@ +use nrpc_build::IPreprocessor; +//use prost_build::{Service, ServiceGenerator}; +use prost_types::FileDescriptorSet; + +use super::SharedState; + +pub struct WasmProtoPreprocessor { + shared: SharedState, +} + +impl WasmProtoPreprocessor { + pub fn with_state(state: &SharedState) -> Self { + Self { + shared: state.clone(), + } + } +} + +impl IPreprocessor for WasmProtoPreprocessor { + fn process(&mut self, fds: &mut FileDescriptorSet) -> proc_macro2::TokenStream { + self.shared.lock() + .expect("Cannot lock shared state") + .fds = Some(fds.clone()); + quote::quote!{} + } +} + diff --git a/usdpl-build/src/front/service_generator.rs b/usdpl-build/src/front/service_generator.rs new file mode 100644 index 0000000..173a757 --- /dev/null +++ b/usdpl-build/src/front/service_generator.rs @@ -0,0 +1,592 @@ +use std::collections::HashSet; + +use prost_build::Service; +use prost_types::{FileDescriptorSet, DescriptorProto, EnumDescriptorProto, FieldDescriptorProto}; +use nrpc_build::IServiceGenerator; + +use super::SharedState; + +pub struct WasmServiceGenerator { + shared: SharedState, +} + +impl WasmServiceGenerator { + pub fn with_state(state: &SharedState) -> Self { + Self { + shared: state.clone(), + } + } +} + +fn generate_service_methods(service: &Service, fds: &FileDescriptorSet) -> proc_macro2::TokenStream { + let mut gen_methods = Vec::with_capacity(service.methods.len()); + for method in &service.methods { + let method_name = quote::format_ident!("{}", method.name); + let method_input = quote::format_ident!("{}{}", &service.name, method.input_type); + let method_output = quote::format_ident!("{}{}", &service.name, method.output_type); + + let input_type = find_message_type(&method.input_type, &service.package, fds).expect("Protobuf message is used but not found"); + + let mut input_params = Vec::with_capacity(input_type.field.len()); + let mut params_to_fields = Vec::with_capacity(input_type.field.len()); + for field in &input_type.field { + //let param_name = quote::format_ident!("val{}", i.to_string()); + let type_name = translate_type(field, &service.name); + let field_name = quote::format_ident!("{}", field.name.as_ref().expect("Protobuf message field needs a name")); + input_params.push(quote::quote!{ + #field_name: #type_name, + }); + params_to_fields.push(quote::quote!{ + #field_name,//: #field_name, + }); + } + let params_to_fields_transformer = if input_type.field.len() == 1 { + let field_name = quote::format_ident!("{}", input_type.field[0].name.as_ref().expect("Protobuf message field needs a name")); + quote::quote!{ + let val = #field_name; + } + } else if input_type.field.is_empty() { + quote::quote!{ + let val = #method_input {}; + } + } else { + quote::quote!{ + let val = #method_input { + #(#params_to_fields)* + }; + } + }; + + let special_fn_into_input = quote::format_ident!("{}_convert_into", method.input_type.split('.').last().unwrap().to_lowercase()); + + let special_fn_from_output = quote::format_ident!("{}_convert_from", method.output_type.split('.').last().unwrap().to_lowercase()); + + gen_methods.push( + quote::quote!{ + #[wasm_bindgen] + pub async fn #method_name(&mut self, #(#input_params)*) -> Option<#method_output> { + + #params_to_fields_transformer + + match self.service.#method_name(#special_fn_into_input(val)).await { + Ok(x) => Some(#special_fn_from_output(x)), + Err(_e) => { + // TODO log error + None + } + } + } + } + ); + } + quote::quote!{ + #(#gen_methods)* + } +} + +fn find_message_type<'a>(want_type: &str, want_package: &str, fds: &'a FileDescriptorSet) -> Option<&'a DescriptorProto> { + for file in &fds.file { + for message_type in &file.message_type { + if let Some(name) = &message_type.name { + if let Some(pkg) = &file.package { + if name == want_type && pkg == want_package { + return Some(message_type); + } + } + } + } + } + None +} + +fn find_enum_type<'a>(want_type: &str, want_package: &str, fds: &'a FileDescriptorSet) -> Option<&'a EnumDescriptorProto> { + for file in &fds.file { + for enum_type in &file.enum_type { + if let Some(name) = &enum_type.name { + if let Some(pkg) = &file.package { + if name == want_type && pkg == want_package { + return Some(enum_type); + } + } + } + } + } + None +} + +fn find_field<'a>(want_field: &str, descriptor: &'a DescriptorProto) -> Option<&'a FieldDescriptorProto> { + for field in &descriptor.field { + if let Some(name) = &field.name { + if name == want_field { + return Some(field); + } + } + } + None +} + +fn translate_type(field: &FieldDescriptorProto, service: &str) -> proc_macro2::TokenStream { + if let Some(type_name) = &field.type_name { + translate_type_name(type_name, service) + } else { + let number = field.r#type.unwrap(); + translate_type_known(number) + } +} + +fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mut HashSet, handled_types: &mut HashSet, is_response_msg: bool, service: &str) -> proc_macro2::TokenStream { + let msg_name = quote::format_ident!("{}{}", service, descriptor.name.as_ref().expect("Protobuf message needs a name")); + let super_msg_name = quote::format_ident!("{}", descriptor.name.as_ref().expect("Protobuf message needs a name")); + let mut gen_fields = Vec::with_capacity(descriptor.field.len()); + let mut gen_into_fields = Vec::with_capacity(descriptor.field.len()); + let mut gen_from_fields = Vec::with_capacity(descriptor.field.len()); + + let mut gen_nested_types = Vec::with_capacity(descriptor.nested_type.len()); + + let mut gen_enums = Vec::with_capacity(descriptor.enum_type.len()); + + if let Some(options) = &descriptor.options { + if let Some(map_entry) = options.map_entry { + // TODO deal with options when necessary + if map_entry { + let name = descriptor.name.clone().expect("Protobuf message needs a name"); + let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase()); + let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase()); + let key_field = find_field("key", descriptor).expect("Protobuf map entry has no key field"); + let key_type = translate_type(&key_field, service); + let value_field = find_field("value", descriptor).expect("Protobuf map entry has no value field"); + let value_type = translate_type(&value_field, service); + return quote::quote!{ + pub type #msg_name = ::js_sys::Map; + + #[inline] + #[allow(dead_code)] + fn #special_fn_from(other: ::std::collections::HashMap<#key_type, #value_type>) -> #msg_name { + let map = #msg_name::new(); + for (key, val) in other.iter() { + map.set(&key.into(), &val.into()); + } + map + } + + #[inline] + #[allow(dead_code)] + fn #special_fn_into(this: #msg_name) -> ::std::collections::HashMap<#key_type, #value_type> { + let mut output = ::std::collections::HashMap::<#key_type, #value_type>::new(); + this.for_each(&mut |key: ::wasm_bindgen::JsValue, val: ::wasm_bindgen::JsValue| { + if let Some(key) = key.as_string() { + if let Some(val) = val.as_string() { + output.insert(key, val); + } + } + }); + output + } + } + } + } else { + todo!("Deal with message options when necessary"); + } + } + + for n_type in &descriptor.nested_type { + let type_name = n_type.name.clone().expect("Protobuf nested message needs a name"); + if !handled_types.contains(&type_name) { + handled_types.insert(type_name); + gen_nested_types.push(generate_wasm_struct_interop(n_type, handled_enums, handled_types, is_response_msg, service)); + } + } + + for e_type in &descriptor.enum_type { + let type_name = e_type.name.clone().expect("Protobuf enum needs a name"); + if !handled_enums.contains(&type_name) { + handled_enums.insert(type_name); + gen_enums.push(generate_wasm_enum_interop(e_type, service)); + } + } + if descriptor.field.len() == 1 { + let field = &descriptor.field[0]; + let field_name = quote::format_ident!("{}", field.name.as_ref().expect("Protobuf message field needs a name")); + let type_name = translate_type(field, service); + gen_fields.push(quote::quote!{ + pub #field_name: #type_name, + }); + if let Some(name) = &field.type_name { + let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase()); + let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase()); + gen_into_fields.push( + quote::quote!{ + #field_name: #special_fn_into(this) + } + ); + + gen_from_fields.push( + quote::quote!{ + #special_fn_from(other.#field_name) + } + ); + } else { + gen_into_fields.push( + quote::quote!{ + #field_name: this + } + ); + + gen_from_fields.push( + quote::quote!{ + other.#field_name + } + ); + } + + let name = descriptor.name.clone().expect("Protobuf message needs a name"); + let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase()); + let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase()); + + quote::quote!{ + pub type #msg_name = #type_name; + + #[inline] + #[allow(dead_code)] + fn #special_fn_from(other: super::#super_msg_name) -> #msg_name { + #(#gen_from_fields)* + } + + #[inline] + #[allow(dead_code)] + fn #special_fn_into(this: #msg_name) -> super::#super_msg_name { + super::#super_msg_name { + #(#gen_into_fields)* + } + } + + #(#gen_nested_types)* + + #(#gen_enums)* + } + } else { + for field in &descriptor.field { + let field_name = quote::format_ident!("{}", field.name.as_ref().expect("Protobuf message field needs a name")); + let type_name = translate_type(field, service); + gen_fields.push(quote::quote!{ + pub #field_name: #type_name, + }); + if let Some(name) = &field.type_name { + let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase()); + let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase()); + gen_into_fields.push( + quote::quote!{ + #field_name: #special_fn_into(self.#field_name), + } + ); + + gen_from_fields.push( + quote::quote!{ + #field_name: #special_fn_from(other.#field_name), + } + ); + } else { + gen_into_fields.push( + quote::quote!{ + #field_name: self.#field_name, + } + ); + + gen_from_fields.push( + quote::quote!{ + #field_name: other.#field_name, + } + ); + } + } + + let name = descriptor.name.clone().expect("Protobuf message needs a name"); + let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase()); + let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase()); + + let wasm_attribute_maybe = if descriptor.field.len() == 1 || !is_response_msg { + quote::quote!{} + } else { + quote::quote!{ + #[wasm_bindgen] + } + }; + + quote::quote!{ + #wasm_attribute_maybe + pub struct #msg_name { + #(#gen_fields)* + } + + impl std::convert::Into for #msg_name { + #[inline] + fn into(self) -> super::#super_msg_name { + super::#super_msg_name { + #(#gen_into_fields)* + } + } + } + + impl std::convert::From for #msg_name { + #[inline] + #[allow(unused_variables)] + fn from(other: super::#super_msg_name) -> Self { + #msg_name { + #(#gen_from_fields)* + } + } + } + + #[inline] + #[allow(dead_code)] + fn #special_fn_from(other: super::#super_msg_name) -> #msg_name { + #msg_name::from(other) + } + + #[inline] + #[allow(dead_code)] + fn #special_fn_into(this: #msg_name) -> super::#super_msg_name { + this.into() + } + + #(#gen_nested_types)* + + #(#gen_enums)* + } + } + +} + +fn translate_type_name(name: &str, service: &str) -> proc_macro2::TokenStream { + match name { + "double" => quote::quote!{f64}, + "float" => quote::quote!{f32}, + "int32" => quote::quote!{i32}, + "int64" => quote::quote!{i64}, + "uint32" => quote::quote!{u32}, + "uint64" => quote::quote!{u64}, + "sint32" => quote::quote!{i32}, + "sint64" => quote::quote!{i64}, + "fixed32" => quote::quote!{u32}, + "fixed64" => quote::quote!{u64}, + "sfixed32" => quote::quote!{i32}, + "sfixed64" => quote::quote!{i64}, + "bool" => quote::quote!{bool}, + "string" => quote::quote!{String}, + "bytes" => quote::quote!{Vec}, + t => { + let ident = quote::format_ident!("{}{}", service, t.split('.').last().unwrap()); + quote::quote!{#ident} + }, + } +} + +fn translate_type_known(id: i32) -> proc_macro2::TokenStream { + match id { + //"double" => quote::quote!{f64}, + //"float" => quote::quote!{f32}, + //"int32" => quote::quote!{i32}, + //"int64" => quote::quote!{i64}, + //"uint32" => quote::quote!{u32}, + //"uint64" => quote::quote!{u64}, + //"sint32" => quote::quote!{i32}, + //"sint64" => quote::quote!{i64}, + //"fixed32" => quote::quote!{u32}, + //"fixed64" => quote::quote!{u64}, + //"sfixed32" => quote::quote!{i32}, + //"sfixed64" => quote::quote!{i64}, + //"bool" => quote::quote!{bool}, + 9 => quote::quote!{String}, + //"bytes" => quote::quote!{Vec}, + t => { + let ident = quote::format_ident!("UnknownType{}", t.to_string()); + quote::quote!{#ident} + }, + } +} + +fn generate_wasm_enum_interop(descriptor: &EnumDescriptorProto, service: &str) -> proc_macro2::TokenStream { + let enum_name = quote::format_ident!("{}{}", service, descriptor.name.as_ref().expect("Protobuf enum needs a name")); + let super_enum_name = quote::format_ident!("{}", descriptor.name.as_ref().expect("Protobuf enum needs a name")); + let mut gen_values = Vec::with_capacity(descriptor.value.len()); + let mut gen_into_values = Vec::with_capacity(descriptor.value.len()); + let mut gen_from_values = Vec::with_capacity(descriptor.value.len()); + if let Some(_options) = &descriptor.options { + // TODO deal with options when necessary + todo!("Deal with enum options when necessary"); + } + for value in &descriptor.value { + let val_name = quote::format_ident!("{}", value.name.as_ref().expect("Protobuf enum value needs a name")); + if let Some(_val_options) = &value.options { + // TODO deal with options when necessary + todo!("Deal with enum value options when necessary"); + } else { + if let Some(number) = &value.number { + gen_values.push( + quote::quote!{ + #val_name = #number, + } + ); + } else { + gen_values.push( + quote::quote!{ + #val_name, + } + ); + } + gen_into_values.push( + quote::quote!{ + Self::#val_name => super::#super_enum_name::#val_name, + } + ); + + gen_from_values.push( + quote::quote!{ + super::#super_enum_name::#val_name => Self::#val_name, + } + ); + } + } + let name = descriptor.name.clone().expect("Protobuf message needs a name"); + let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase()); + let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase()); + + quote::quote!{ + #[wasm_bindgen] + #[repr(i32)] + #[derive(Clone, Copy)] + pub enum #enum_name { + #(#gen_values)* + } + + impl std::convert::Into for #enum_name { + fn into(self) -> super::#super_enum_name { + match self { + #(#gen_into_values)* + } + } + } + + impl std::convert::From for #enum_name { + fn from(other: super::#super_enum_name) -> Self { + match other { + #(#gen_from_values)* + } + } + } + + #[inline] + #[allow(dead_code)] + fn #special_fn_from(other: i32) -> #enum_name { + #enum_name::from(super::#super_enum_name::from_i32(other).unwrap()) + } + + #[inline] + #[allow(dead_code)] + fn #special_fn_into(this: #enum_name) -> i32 { + this as i32 + } + } +} + +fn generate_service_io_types(service: &Service, fds: &FileDescriptorSet) -> proc_macro2::TokenStream { + let mut gen_types = Vec::with_capacity(service.methods.len() * 2); + let mut gen_enums = Vec::new(); + let mut handled_enums = HashSet::new(); + let mut handled_types = HashSet::new(); + for method in &service.methods { + if let Some(input_message) = find_message_type(&method.input_type, &service.package, fds) { + let msg_name = input_message.name.clone().expect("Protobuf message name required"); + if !handled_types.contains(&msg_name) { + handled_types.insert(msg_name); + gen_types.push(generate_wasm_struct_interop(input_message, &mut handled_enums, &mut handled_types, false, &service.name)); + } + } else if let Some(input_enum) = find_enum_type(&method.input_type, &service.package, fds) { + let enum_name = input_enum.name.clone().expect("Protobuf enum name required"); + if !handled_enums.contains(&enum_name) { + handled_enums.insert(enum_name); + gen_types.push(generate_wasm_enum_interop(input_enum, &service.name)); + } + } else { + panic!("Cannot find proto type {}/{}", service.package, method.input_type); + } + + if let Some(output_message) = find_message_type(&method.output_type, &service.package, fds) { + let msg_name = output_message.name.clone().expect("Protobuf message name required"); + if !handled_types.contains(&msg_name) { + handled_types.insert(msg_name); + gen_types.push(generate_wasm_struct_interop(output_message, &mut handled_enums, &mut handled_types, true, &service.name)); + } + } else if let Some(output_enum) = find_enum_type(&method.output_type, &service.package, fds) { + let enum_name = output_enum.name.clone().expect("Protobuf enum name required"); + if !handled_enums.contains(&enum_name) { + handled_enums.insert(enum_name); + gen_types.push(generate_wasm_enum_interop(output_enum, &service.name)); + } + } else { + panic!("Cannot find proto type {}/{}", service.package, method.input_type); + } + } + + // always generate all enums, since they aren't encountered (ever, afaik) when generating message structs + for file in &fds.file { + for enum_type in &file.enum_type { + let enum_name = enum_type.name.clone().expect("Protobuf enum name required"); + if !handled_enums.contains(&enum_name) { + handled_enums.insert(enum_name); + gen_enums.push(generate_wasm_enum_interop(enum_type, &service.name)); + } + } + } + quote::quote! { + #(#gen_types)* + + #(#gen_enums)* + } +} + +impl IServiceGenerator for WasmServiceGenerator { + fn generate(&mut self, service: Service) -> proc_macro2::TokenStream { + let lock = self.shared.lock() + .expect("Cannot lock shared state"); + let fds = lock.fds + .as_ref() + .expect("FileDescriptorSet required for WASM service generator"); + let service_struct_name = quote::format_ident!("{}Client", service.name); + let service_js_name = quote::format_ident!("{}", service.name); + let service_methods = generate_service_methods(&service, fds); + let service_types = generate_service_io_types(&service, fds); + let mod_name = quote::format_ident!("js_{}", service.name.to_lowercase()); + quote::quote!{ + mod #mod_name { + use wasm_bindgen::prelude::*; + + use crate::client_handler::WebSocketHandler; + + #service_types + + /// WASM/JS-compatible wrapper of the Rust nRPC service + #[wasm_bindgen] + pub struct #service_js_name { + //#[wasm_bindgen(skip)] + service: super::#service_struct_name, + } + + #[wasm_bindgen] + impl #service_js_name { + #[wasm_bindgen(constructor)] + pub fn new(port: u16) -> Self { + let implementation = super::#service_struct_name::new( + WebSocketHandler::new(port) + ); + Self { + service: implementation, + } + } + + #service_methods + } + } + } + } +} diff --git a/usdpl-build/src/front/shared_state.rs b/usdpl-build/src/front/shared_state.rs new file mode 100644 index 0000000..2175d1d --- /dev/null +++ b/usdpl-build/src/front/shared_state.rs @@ -0,0 +1,26 @@ +use std::sync::{Arc, Mutex}; + +use prost_types::FileDescriptorSet; + +#[derive(Clone)] +pub struct SharedState(Arc>); + +impl SharedState { + pub fn new() -> Self { + Self(Arc::new(Mutex::new(SharedProtoData { + fds: None, + }))) + } +} + +impl std::ops::Deref for SharedState { + type Target = Arc>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub struct SharedProtoData { + pub fds: Option, +} diff --git a/usdpl-build/src/lib.rs b/usdpl-build/src/lib.rs new file mode 100644 index 0000000..31f742f --- /dev/null +++ b/usdpl-build/src/lib.rs @@ -0,0 +1,5 @@ +pub mod back; +pub mod front; + +mod proto_files; +pub use proto_files::{dump_protos, dump_protos_out, proto_out_path, all_proto_filenames}; diff --git a/usdpl-build/src/proto_files.rs b/usdpl-build/src/proto_files.rs new file mode 100644 index 0000000..818c728 --- /dev/null +++ b/usdpl-build/src/proto_files.rs @@ -0,0 +1,44 @@ +use std::path::{Path, PathBuf}; + +struct IncludedFileStr<'a> { + filename: &'a str, + contents: &'a str, +} + +const DEBUG_PROTO: IncludedFileStr<'static> = IncludedFileStr { + filename: "debug.proto", + contents: include_str!("../protos/debug.proto"), +}; + +const TRANSLATIONS_PROTO: IncludedFileStr<'static> = IncludedFileStr { + filename: "translations.proto", + contents: include_str!("../protos/translations.proto"), +}; + +const ALL_PROTOS: [IncludedFileStr<'static>; 2] = [ + DEBUG_PROTO, + TRANSLATIONS_PROTO, +]; + +pub fn proto_out_path() -> PathBuf { + PathBuf::from(std::env::var("OUT_DIR").expect("Not in a build.rs context (missing $OUT_DIR)")).join("protos") +} + +pub fn all_proto_filenames() -> impl Iterator { + ALL_PROTOS.iter().map(|x| x.filename) +} + +pub fn dump_protos(p: impl AsRef) -> std::io::Result<()> { + let p = p.as_ref(); + for f in ALL_PROTOS { + let fullpath = p.join(f.filename); + std::fs::write(fullpath, f.contents)?; + } + Ok(()) +} + +pub fn dump_protos_out() -> std::io::Result<()> { + let path = proto_out_path(); + std::fs::create_dir_all(&path)?; + dump_protos(&path) +} diff --git a/usdpl-core/Cargo.toml b/usdpl-core/Cargo.toml index 5efa156..b196650 100644 --- a/usdpl-core/Cargo.toml +++ b/usdpl-core/Cargo.toml @@ -1,22 +1,21 @@ [package] name = "usdpl-core" -version = "0.10.0" +version = "0.11.0" edition = "2021" license = "GPL-3.0-only" repository = "https://github.com/NGnius/usdpl-rs" -readme = "README.md" +readme = "../README.md" description = "Universal Steam Deck Plugin Library core" [features] default = [] decky = [] -crankshaft = [] encrypt = ["aes-gcm-siv"] -translate = [] [dependencies] base64 = "0.13" aes-gcm-siv = { version = "0.10", optional = true, default-features = false, features = ["alloc", "aes"] } +# nrpc = "0.2" [dev-dependencies] hex-literal = "0.3.4" diff --git a/usdpl-core/src/api_common/target.rs b/usdpl-core/src/api_common/target.rs index ed7f3ca..d34e502 100644 --- a/usdpl-core/src/api_common/target.rs +++ b/usdpl-core/src/api_common/target.rs @@ -4,8 +4,6 @@ pub enum Platform { Any, /// Decky aka PluginLoader platform Decky, - /// Crankshaft platform - Crankshaft, } impl Platform { @@ -16,10 +14,6 @@ impl Platform { { Self::Decky } - #[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))] - { - Self::Crankshaft - } #[cfg(not(any(feature = "decky", feature = "crankshaft")))] { Self::Any @@ -32,7 +26,6 @@ impl std::fmt::Display for Platform { match self { Self::Any => write!(f, "any"), Self::Decky => write!(f, "decky"), - Self::Crankshaft => write!(f, "crankshaft"), } } } diff --git a/usdpl-core/src/api_crankshaft/mod.rs b/usdpl-core/src/api_crankshaft/mod.rs deleted file mode 100644 index 8b13789..0000000 --- a/usdpl-core/src/api_crankshaft/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/usdpl-core/src/socket.rs b/usdpl-core/src/socket.rs index 5a28762..0f72a01 100644 --- a/usdpl-core/src/socket.rs +++ b/usdpl-core/src/socket.rs @@ -40,10 +40,8 @@ pub enum Packet { /// Many packets merged into one Many(Vec), /// Translation data dump - #[cfg(feature = "translate")] Translations(Vec<(String, Vec)>), /// Request translations for language - #[cfg(feature = "translate")] Language(String), } @@ -59,9 +57,7 @@ impl Packet { Self::Unsupported => 6, Self::Bad => 7, Self::Many(_) => 8, - #[cfg(feature = "translate")] Self::Translations(_) => 9, - #[cfg(feature = "translate")] Self::Language(_) => 10, } } @@ -93,12 +89,10 @@ impl Loadable for Packet { let (obj, len) = <_>::load(buf)?; (Self::Many(obj), len) }, - #[cfg(feature = "translate")] 9 => { let (obj, len) = <_>::load(buf)?; (Self::Translations(obj), len) }, - #[cfg(feature = "translate")] 10 => { let (obj, len) = <_>::load(buf)?; (Self::Language(obj), len) @@ -122,9 +116,7 @@ impl Dumpable for Packet { Self::Unsupported => Ok(0), Self::Bad => return Err(DumpError::Unsupported), Self::Many(v) => v.dump(buf), - #[cfg(feature = "translate")] Self::Translations(tr) => tr.dump(buf), - #[cfg(feature = "translate")] Self::Language(l) => l.dump(buf), }?; Ok(size1 + result) diff --git a/usdpl-front/Cargo.toml b/usdpl-front/Cargo.toml index 81b42da..eaea0ef 100644 --- a/usdpl-front/Cargo.toml +++ b/usdpl-front/Cargo.toml @@ -1,27 +1,27 @@ [package] name = "usdpl-front" -version = "0.10.1" +version = "0.11.0" authors = ["NGnius (Graham) "] edition = "2021" license = "GPL-3.0-only" repository = "https://github.com/NGnius/usdpl-rs" -readme = "README.md" +readme = "../README.md" description = "Universal Steam Deck Plugin Library front-end designed for WASM" [lib] crate-type = ["cdylib", "rlib"] [features] -default = ["translate"] +default = [] decky = ["usdpl-core/decky"] -crankshaft = ["usdpl-core/crankshaft"] debug = ["console_error_panic_hook"] encrypt = ["usdpl-core/encrypt", "obfstr", "hex"] -translate = ["usdpl-core/translate"] [dependencies] wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" +gloo-net = { version = "0.2", features = ["websocket"] } +futures = "0.3" # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires @@ -39,10 +39,17 @@ web-sys = { version = "0.3", features = [ ]} js-sys = { version = "0.3" } +async-channel = "1.8" + obfstr = { version = "0.3", optional = true } hex = { version = "0.4", optional = true } -usdpl-core = { version = "0.10", path = "../usdpl-core" } +nrpc = "0.2" +usdpl-core = { version = "0.11", path = "../usdpl-core" } +prost = "0.11" [dev-dependencies] wasm-bindgen-test = { version = "0.3.13" } + +[build-dependencies] +usdpl-build = { version = "0.11", path = "../usdpl-build" } diff --git a/usdpl-front/build.rs b/usdpl-front/build.rs new file mode 100644 index 0000000..16e4f14 --- /dev/null +++ b/usdpl-front/build.rs @@ -0,0 +1,3 @@ +fn main() { + usdpl_build::front::build() +} diff --git a/usdpl-front/src/client_handler.rs b/usdpl-front/src/client_handler.rs new file mode 100644 index 0000000..d123128 --- /dev/null +++ b/usdpl-front/src/client_handler.rs @@ -0,0 +1,81 @@ +use std::sync::atomic::{AtomicU64, Ordering}; + +use nrpc::{ClientHandler, ServiceError, _helpers::bytes, _helpers::async_trait}; +use gloo_net::websocket::{Message, futures::WebSocket}; +use wasm_bindgen_futures::spawn_local; +use futures::{SinkExt, StreamExt}; + +static LAST_ID: AtomicU64 = AtomicU64::new(0); + +pub struct WebSocketHandler { + // TODO + port: u16, +} + +async fn send_recv_ws(url: String, input: bytes::Bytes) -> Result, String> { + let mut ws = WebSocket::open(&url).map_err(|e| e.to_string())?; + ws.send(Message::Bytes(input.into())).await.map_err(|e| e.to_string())?; + + read_next_incoming(ws).await +} + +async fn read_next_incoming(mut ws: WebSocket) -> Result, String> { + if let Some(msg) = ws.next().await { + match msg.map_err(|e| e.to_string())? { + Message::Bytes(b) => Ok(b), + Message::Text(_) => Err("Message::Text not allowed".into()), + } + } else { + Err("No response received".into()) + } +} + +#[derive(Debug)] +struct ErrorStr(String); + +impl std::fmt::Display for ErrorStr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Error message: {}", self.0) + } +} + +impl std::error::Error for ErrorStr {} + +impl WebSocketHandler { + #[allow(dead_code)] + pub fn new(port: u16) -> Self { + Self { port } + } +} + +#[async_trait::async_trait] +impl ClientHandler for WebSocketHandler { + async fn call(&mut self, + service: &str, + method: &str, + input: bytes::Bytes, + output: &mut bytes::BytesMut) -> Result<(), ServiceError> { + let id = LAST_ID.fetch_add(1, Ordering::SeqCst); + let url = format!( + "ws://usdpl-ws-{}.localhost:{}/{}/{}", + id, + self.port, + service, + method, + ); + let (tx, rx) = async_channel::bounded(1); + spawn_local(async move { + tx.send(send_recv_ws( + url, + input + ).await).await.unwrap_or(()); + }); + + output.extend_from_slice( + &rx.recv().await + .map_err(|e| ServiceError::Method(Box::new(e)))? + .map_err(|e| ServiceError::Method(Box::new(ErrorStr(e))))? + ); + Ok(()) + } +} diff --git a/usdpl-front/src/lib.rs b/usdpl-front/src/lib.rs index 3516977..dd173f2 100644 --- a/usdpl-front/src/lib.rs +++ b/usdpl-front/src/lib.rs @@ -5,10 +5,16 @@ //! #![warn(missing_docs)] +mod client_handler; mod connection; mod convert; mod imports; +#[allow(missing_docs)] // existence is pain otherwise +pub mod _nrpc_js_interop { + include!(concat!(env!("OUT_DIR"), "/usdpl.rs")); +} + use std::sync::atomic::{AtomicU64, Ordering}; use js_sys::Array; @@ -19,7 +25,7 @@ use usdpl_core::{socket::Packet, RemoteCall}; //const REMOTE_PORT: std::sync::atomic::AtomicU16 = std::sync::atomic::AtomicU16::new(31337); static mut CTX: UsdplContext = UsdplContext { - port: 31337, + port: 0, id: AtomicU64::new(0), #[cfg(feature = "encrypt")] key: Vec::new(), @@ -27,7 +33,6 @@ static mut CTX: UsdplContext = UsdplContext { static mut CACHE: Option> = None; -#[cfg(feature = "translate")] static mut TRANSLATIONS: Option>> = None; #[cfg(feature = "encrypt")] From 6a525fa38410931d7cae46a650c169befa58be39 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sun, 23 Apr 2023 23:03:10 -0400 Subject: [PATCH 02/12] Add barebones nRPC over websocket adapters and functionality --- Cargo.lock | 537 ++++++++++++++---------------- usdpl-back/Cargo.toml | 12 +- usdpl-back/src/callable.rs | 87 ----- usdpl-back/src/instance.rs | 262 --------------- usdpl-back/src/lib.rs | 11 +- usdpl-back/src/rpc/mod.rs | 2 + usdpl-back/src/rpc/registry.rs | 42 +++ usdpl-back/src/websockets.rs | 108 ++++++ usdpl-build/Cargo.toml | 2 +- usdpl-front/Cargo.toml | 2 +- usdpl-front/src/client_handler.rs | 4 +- 11 files changed, 417 insertions(+), 652 deletions(-) delete mode 100644 usdpl-back/src/callable.rs delete mode 100644 usdpl-back/src/instance.rs create mode 100644 usdpl-back/src/rpc/registry.rs create mode 100644 usdpl-back/src/websockets.rs diff --git a/Cargo.lock b/Cargo.lock index 8af7fe1..00f5318 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aead" version = "0.4.3" @@ -55,6 +61,15 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-lock" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] + [[package]] name = "async-recursion" version = "1.0.4" @@ -89,12 +104,6 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" -[[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" - [[package]] name = "beef" version = "0.5.2" @@ -109,9 +118,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" -version = "0.10.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ "generic-array", ] @@ -174,6 +183,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "cpufeatures" version = "0.2.6" @@ -183,6 +198,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-utils" version = "0.8.15" @@ -192,16 +216,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - [[package]] name = "ctr" version = "0.8.0" @@ -212,13 +226,25 @@ dependencies = [ ] [[package]] -name = "digest" -version = "0.10.6" +name = "derive_more" +version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "block-buffer", - "crypto-common", + "convert_case", + "proc-macro2", + "quote", + "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]] @@ -333,6 +359,17 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "libz-sys", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -501,56 +538,12 @@ dependencies = [ "web-sys", ] -[[package]] -name = "h2" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "headers" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" -dependencies = [ - "base64 0.13.1", - "bitflags", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - [[package]] name = "heck" version = "0.4.1" @@ -595,53 +588,12 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - [[package]] name = "httparse" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "hyper" -version = "0.14.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "idna" version = "0.3.0" @@ -718,12 +670,33 @@ version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +[[package]] +name = "libz-sys" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.17" @@ -795,19 +768,12 @@ dependencies = [ ] [[package]] -name = "mime" -version = "0.3.17" +name = "miniz_oxide" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ - "mime", - "unicase", + "adler", ] [[package]] @@ -828,25 +794,11 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" -[[package]] -name = "multiparty" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed1ec6589a6d4a1e0b33b4c0a3f6ee96dfba88ebdb3da51403fd7cf0a24a4b04" -dependencies = [ - "bytes", - "futures-core", - "httparse", - "memchr", - "pin-project-lite", - "try-lock", -] - [[package]] name = "nrpc" -version = "0.2.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8770a9a12e53035a536e41b7899d72bc423b2b8c17402c8f0dbe44d6bb255ac6" +checksum = "6f41caeb65399490c6f68ab2527a833d6f2e9b0d7d5ffc4b062f1484b3fa61cd" dependencies = [ "async-trait", "bytes", @@ -855,9 +807,9 @@ dependencies = [ [[package]] name = "nrpc-build" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5914c2bfd187a4bdfa7a69330e1ef4d8dea4060d8a08bf146f622aa7f176680" +checksum = "5b598ecce0e6d4b2cb367143696174ae24bff5eb4aeb1d8eccffbfeef75fc68e" dependencies = [ "nrpc", "prettyplease 0.2.4", @@ -897,6 +849,29 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "windows-sys 0.45.0", +] + [[package]] name = "percent-encoding" version = "2.2.0" @@ -945,6 +920,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + [[package]] name = "polyval" version = "0.5.3" @@ -1125,6 +1106,75 @@ dependencies = [ "getrandom", ] +[[package]] +name = "ratchet_core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854bf6632d9f5c7fa7f77cbc332f2b0a8dfb2acc36c3f351fc36bf40f2759728" +dependencies = [ + "base64", + "bitflags", + "bytes", + "derive_more", + "either", + "fnv", + "http", + "httparse", + "log", + "rand", + "ratchet_ext", + "sha-1", + "thiserror", + "tokio", + "tokio-util", + "url", +] + +[[package]] +name = "ratchet_deflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b144cb23a76d810b25737f4b87943fdfd7772b423bdc15c2b3820849207adc" +dependencies = [ + "bytes", + "flate2", + "http", + "log", + "ratchet_ext", + "thiserror", +] + +[[package]] +name = "ratchet_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67f97bb0776d195720319a1e9f08fa343fe3f9f0b7ebf9d97d5926ce50b8e1ad" +dependencies = [ + "bytes", + "http", + "httparse", +] + +[[package]] +name = "ratchet_rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7dba456fc23026b46ce0936d109ce3e73b4a592baf0dda0f83d49886c5e5f83" +dependencies = [ + "ratchet_core", + "ratchet_deflate", + "ratchet_ext", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1149,6 +1199,15 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.37.11" @@ -1163,15 +1222,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" -dependencies = [ - "base64 0.21.0", -] - [[package]] name = "ryu" version = "1.0.13" @@ -1184,6 +1234,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + [[package]] name = "serde" version = "1.0.160" @@ -1202,26 +1264,25 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "sha-1" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ + "block-buffer", "cfg-if", "cpufeatures", "digest", + "opaque-debug", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", ] [[package]] @@ -1233,6 +1294,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + [[package]] name = "socket2" version = "0.4.9" @@ -1279,7 +1346,7 @@ checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", "windows-sys 0.45.0", ] @@ -1330,98 +1397,37 @@ dependencies = [ "libc", "mio", "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", + "tokio-macros", "windows-sys 0.45.0", ] [[package]] -name = "tokio-stream" -version = "0.1.12" +name = "tokio-macros" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", + "proc-macro2", + "quote", + "syn 2.0.15", ] [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ "bytes", "futures-core", "futures-sink", + "log", "pin-project-lite", "tokio", - "tracing", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "tungstenite" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" -dependencies = [ - "base64 0.13.1", - "byteorder", - "bytes", - "http", - "httparse", - "log", - "rand", - "sha1", - "thiserror", - "url", - "utf-8", ] [[package]] @@ -1430,15 +1436,6 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - [[package]] name = "unicode-bidi" version = "0.3.13" @@ -1491,6 +1488,7 @@ dependencies = [ name = "usdpl-back" version = "0.11.0" dependencies = [ + "async-lock", "async-recursion", "async-trait", "bytes", @@ -1499,10 +1497,10 @@ dependencies = [ "log", "nrpc", "obfstr", + "ratchet_rs", "tokio", "usdpl-build", "usdpl-core", - "warp", ] [[package]] @@ -1523,7 +1521,7 @@ name = "usdpl-core" version = "0.11.0" dependencies = [ "aes-gcm-siv", - "base64 0.13.1", + "base64", "hex-literal", ] @@ -1549,10 +1547,10 @@ dependencies = [ ] [[package]] -name = "utf-8" -version = "0.7.6" +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" @@ -1560,47 +1558,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "warp" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27e1a710288f0f91a98dd8a74f05b76a10768db245ce183edf64dc1afdc3016c" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "headers", - "http", - "hyper", - "log", - "mime", - "mime_guess", - "multiparty", - "percent-encoding", - "pin-project", - "rustls-pemfile", - "scoped-tls", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-stream", - "tokio-tungstenite", - "tokio-util", - "tower-service", - "tracing", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/usdpl-back/Cargo.toml b/usdpl-back/Cargo.toml index 4941d35..26ba550 100644 --- a/usdpl-back/Cargo.toml +++ b/usdpl-back/Cargo.toml @@ -10,7 +10,7 @@ description = "Universal Steam Deck Plugin Library back-end" [features] default = ["blocking"] decky = ["usdpl-core/decky"] -blocking = ["tokio", "tokio/rt", "tokio/rt-multi-thread"] # synchronous API for async functionality, using tokio +blocking = [] # synchronous API for async functionality, using tokio encrypt = ["usdpl-core/encrypt", "obfstr", "hex"] [dependencies] @@ -19,12 +19,16 @@ usdpl-core = { version = "0.11", path = "../usdpl-core"} log = "0.4" # gRPC/protobuf -nrpc = "0.2" +nrpc = "0.6" +async-lock = "2.7" + +# websocket framework +ratchet_rs = { version = "0.3", features = [ "deflate" ] } # HTTP web framework -warp = { version = "0.3" } +#warp = { version = "0.3" } bytes = { version = "1.1" } -tokio = { version = "1", optional = true } +tokio = { version = "1", features = [ "full" ]} # this is why people don't like async async-trait = "0.1.57" diff --git a/usdpl-back/src/callable.rs b/usdpl-back/src/callable.rs deleted file mode 100644 index 2a954c2..0000000 --- a/usdpl-back/src/callable.rs +++ /dev/null @@ -1,87 +0,0 @@ -use std::sync::{Arc, Mutex}; - -use usdpl_core::serdes::Primitive; - -/// A mutable function which can be called from the front-end (remotely) -pub trait MutCallable: Send + Sync { - /// Invoke the function - fn call(&mut self, params: Vec) -> Vec; -} - -impl) -> Vec) + Send + Sync> MutCallable for F { - fn call(&mut self, params: Vec) -> Vec { - (self)(params) - } -} - -/// A function which can be called from the front-end (remotely) -pub trait Callable: Send + Sync { - /// Invoke the function - fn call(&self, params: Vec) -> Vec; -} - -impl) -> Vec) + Send + Sync> Callable for F { - fn call(&self, params: Vec) -> Vec { - (self)(params) - } -} - -/// An async function which can be called from the front-end (remotely) -#[async_trait::async_trait] -pub trait AsyncCallable: Send + Sync { - /// Invoke the function - async fn call(&self, params: Vec) -> Vec; -} - -#[async_trait::async_trait] -impl) -> A) + Send + Sync, A: core::future::Future> + Send> AsyncCallable for F { - async fn call(&self, params: Vec) -> Vec { - (self)(params).await - } -} - -pub enum WrappedCallable { - Blocking(Arc>>), - Ref(Arc>), - Async(Arc>), -} - -impl WrappedCallable { - pub fn new_ref(callable: T) -> Self { - Self::Ref(Arc::new(Box::new(callable))) - } - - pub fn new_locking(callable: T) -> Self { - Self::Blocking(Arc::new(Mutex::new(Box::new(callable)))) - } - - pub fn new_async(callable: T) -> Self { - Self::Async(Arc::new(Box::new(callable))) - } -} - -impl Clone for WrappedCallable { - fn clone(&self) -> Self { - match self { - Self::Blocking(x) => Self::Blocking(x.clone()), - Self::Ref(x) => Self::Ref(x.clone()), - Self::Async(x) => Self::Async(x.clone()), - } - } -} - -#[async_trait::async_trait] -impl AsyncCallable for WrappedCallable { - async fn call(&self, params: Vec) -> Vec { - match self { - Self::Blocking(mut_callable) => { - mut_callable - .lock() - .expect("Failed to acquire mut_callable lock") - .call(params) - }, - Self::Ref(callable) => callable.call(params), - Self::Async(async_callable) => async_callable.call(params).await, - } - } -} diff --git a/usdpl-back/src/instance.rs b/usdpl-back/src/instance.rs deleted file mode 100644 index 7dd842e..0000000 --- a/usdpl-back/src/instance.rs +++ /dev/null @@ -1,262 +0,0 @@ -use std::collections::HashMap; -use std::sync::atomic::{AtomicU64, Ordering}; - -use warp::Filter; - -use usdpl_core::serdes::{Dumpable, Loadable}; -use usdpl_core::{socket, RemoteCallResponse}; - -use super::{Callable, MutCallable, AsyncCallable, WrappedCallable}; - -static LAST_ID: AtomicU64 = AtomicU64::new(0); -const MAX_ID_DIFFERENCE: u64 = 32; - -//type WrappedCallable = Arc>>; // thread-safe, cloneable Callable - -#[cfg(feature = "encrypt")] -const NONCE: [u8; socket::NONCE_SIZE] = [0u8; socket::NONCE_SIZE]; - -/// Back-end instance for interacting with the front-end -pub struct Instance { - calls: HashMap, - port: u16, - #[cfg(feature = "encrypt")] - encryption_key: Vec, -} - -impl Instance { - /// Initialise an instance of the back-end - #[inline] - pub fn new(port_usdpl: u16) -> Self { - Instance { - calls: HashMap::new(), - port: port_usdpl, - #[cfg(feature = "encrypt")] - encryption_key: hex::decode(obfstr::obfstr!(env!("USDPL_ENCRYPTION_KEY"))).unwrap(), - } - } - - /// Register a thread-safe function which can be invoked by the front-end - pub fn register, F: Callable + 'static>( - mut self, - name: S, - f: F, - ) -> Self { - self.calls - .insert(name.into(), WrappedCallable::new_ref(f)); - self - } - - /// Register a thread-unsafe function which can be invoked by the front-end - pub fn register_blocking, F: MutCallable + 'static>( - mut self, - name: S, - f: F, - ) -> Self { - self.calls - .insert(name.into(), WrappedCallable::new_locking(f)); - self - } - - /// Register a thread-unsafe function which can be invoked by the front-end - pub fn register_async, F: AsyncCallable + 'static>( - mut self, - name: S, - f: F, - ) -> Self { - self.calls - .insert(name.into(), WrappedCallable::new_async(f)); - self - } - - /// Run the web server instance forever, blocking this thread - #[cfg(feature = "blocking")] - pub fn run_blocking(&self) -> Result<(), ()> { - let result = self.serve_internal(); - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap() - .block_on(result) - } - - /// Run the web server forever, asynchronously - pub async fn run(&self) -> Result<(), ()> { - self.serve_internal().await - } - - #[async_recursion::async_recursion] - async fn handle_call( - packet: socket::Packet, - handlers: &HashMap, - ) -> socket::Packet { - match packet { - socket::Packet::Call(call) => { - log::debug!("Got USDPL call {} (`{}`, params: {})", call.id, call.function, call.parameters.len()); - let last_id = LAST_ID.load(Ordering::SeqCst); - if last_id == 0 { - log::info!("Last id is 0, assuming resumed connection (overriding last id)"); - LAST_ID.store(call.id, Ordering::SeqCst); - } else if call.id < MAX_ID_DIFFERENCE { - log::info!("Call ID is low, assuming new connection (resetting last id)"); - LAST_ID.store(call.id, Ordering::SeqCst); - } else if call.id > last_id && call.id - last_id < MAX_ID_DIFFERENCE { - LAST_ID.store(call.id, Ordering::SeqCst); - } else if call.id < last_id && last_id - call.id < MAX_ID_DIFFERENCE { - // Allowed, but don't store new (lower) LAST_ID - } else { - #[cfg(not(debug_assertions))] - { - log::error!("Got USDPL call with strange ID! got:{} last id:{} (rejecting packet)", call.id, last_id); - return socket::Packet::Invalid - } - #[cfg(debug_assertions)] - log::warn!("Got USDPL call with strange ID! got:{} last id:{} (in release mode this packet will be rejected)", call.id, last_id); - } - //let handlers = CALLS.lock().expect("Failed to acquire CALLS lock"); - if let Some(target) = handlers.get(&call.function) { - let result = target.call(call.parameters).await; - socket::Packet::CallResponse(RemoteCallResponse { - id: call.id, - response: result, - }) - } else { - socket::Packet::Invalid - } - }, - socket::Packet::Many(packets) => { - let mut result = Vec::with_capacity(packets.len()); - for packet in packets { - result.push(Self::handle_call(packet, handlers).await); - } - socket::Packet::Many(result) - }, - #[cfg(feature = "translate")] - socket::Packet::Language(lang) => socket::Packet::Translations(get_all_translations(lang)), - _ => socket::Packet::Invalid, - } - } - - #[cfg(not(feature = "encrypt"))] - async fn process_body((data, handlers): (bytes::Bytes, HashMap)) -> impl warp::Reply { - let (packet, _) = match socket::Packet::load_base64(&data) { - Ok(x) => x, - Err(e) => { - return warp::reply::with_status( - warp::http::Response::builder() - .body(format!("Failed to load packet: {}", e)), - warp::http::StatusCode::from_u16(400).unwrap(), - ) - } - }; - //let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE]; - let mut buffer = String::with_capacity(socket::PACKET_BUFFER_SIZE); - let response = Self::handle_call(packet, &handlers).await; - let _len = match response.dump_base64(&mut buffer) { - Ok(x) => x, - Err(e) => { - return warp::reply::with_status( - warp::http::Response::builder() - .body(format!("Failed to dump response packet: {}", e)), - warp::http::StatusCode::from_u16(500).unwrap(), - ) - } - }; - warp::reply::with_status( - warp::http::Response::builder().body(buffer), - warp::http::StatusCode::from_u16(200).unwrap(), - ) - } - - #[cfg(feature = "encrypt")] - async fn process_body((data, handlers, key): (bytes::Bytes, HashMap, Vec)) -> impl warp::Reply { - let (packet, _) = match socket::Packet::load_encrypted(&data, &key, &NONCE) { - Ok(x) => x, - Err(_) => { - return warp::reply::with_status( - warp::http::Response::builder() - .body("Failed to load packet".to_string()), - warp::http::StatusCode::from_u16(400).unwrap(), - ) - } - }; - let mut buffer = Vec::with_capacity(socket::PACKET_BUFFER_SIZE); - //buffer.extend(&[0u8; socket::PACKET_BUFFER_SIZE]); - let response = Self::handle_call(packet, &handlers).await; - let len = match response.dump_encrypted(&mut buffer, &key, &NONCE) { - Ok(x) => x, - Err(_) => { - return warp::reply::with_status( - warp::http::Response::builder() - .body("Failed to dump response packet".to_string()), - warp::http::StatusCode::from_u16(500).unwrap(), - ) - } - }; - buffer.truncate(len); - let string: String = String::from_utf8(buffer).unwrap().into(); - warp::reply::with_status( - warp::http::Response::builder().body(string), - warp::http::StatusCode::from_u16(200).unwrap(), - ) - } - - /// Receive and execute callbacks forever - async fn serve_internal(&self) -> Result<(), ()> { - let handlers = self.calls.clone(); - #[cfg(not(feature = "encrypt"))] - let input_mapper = move |data: bytes::Bytes| { (data, handlers.clone()) }; - #[cfg(feature = "encrypt")] - let key = self.encryption_key.clone(); - #[cfg(feature = "encrypt")] - let input_mapper = move |data: bytes::Bytes| { (data, handlers.clone(), key.clone()) }; - //self.calls = HashMap::new(); - let calls = warp::post() - .and(warp::path!("usdpl" / "call")) - .and(warp::body::content_length_limit( - (socket::PACKET_BUFFER_SIZE * 2) as _, - )) - .and(warp::body::bytes()) - .map(input_mapper) - .then(Self::process_body) - .map(|reply| warp::reply::with_header(reply, "Access-Control-Allow-Origin", "*")); - #[cfg(debug_assertions)] - warp::serve(calls).run(([0, 0, 0, 0], self.port)).await; - #[cfg(not(debug_assertions))] - warp::serve(calls).run(([127, 0, 0, 1], self.port)).await; - Ok(()) - } -} - -#[cfg(feature = "translate")] -fn get_all_translations(language: String) -> Vec<(String, Vec)> { - log::debug!("Loading translations for language `{}`...", language); - let result = load_locale(&language); - match result { - Ok(catalog) => { - let map = catalog.nalltext(); - let mut result = Vec::with_capacity(map.len()); - for (key, val) in map.iter() { - result.push((key.to_owned().into(), val.iter().map(|x| x.into()).collect())); - } - result - }, - Err(e) => { - log::error!("Failed to load translations for language `{}`: {}", language, e); - vec![] - } - } -} - -#[cfg(feature = "translate")] -fn load_locale(lang: &str) -> Result { - let path = crate::api::dirs::plugin().unwrap_or_else(|| "".into()).join("translations").join(format!("{}.mo", lang)); - let file = std::fs::File::open(path).map_err(|e| gettext_ng::Error::Io(e))?; - gettext_ng::Catalog::parse(file) -} - -#[cfg(test)] -mod tests { - #[allow(unused_imports)] - use super::*; -} diff --git a/usdpl-back/src/lib.rs b/usdpl-back/src/lib.rs index 2771cf7..fc4c052 100644 --- a/usdpl-back/src/lib.rs +++ b/usdpl-back/src/lib.rs @@ -13,13 +13,12 @@ mod api_crankshaft; #[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))] mod api_decky; -mod callable; -//mod errors; -mod instance; +mod rpc; -pub use callable::{Callable, MutCallable, AsyncCallable}; -pub(crate) use callable::WrappedCallable; -pub use instance::Instance; +//mod errors; +mod websockets; + +pub use websockets::WebsocketServer as Server; //pub use errors::{ServerError, ServerResult}; /// USDPL backend API. diff --git a/usdpl-back/src/rpc/mod.rs b/usdpl-back/src/rpc/mod.rs index e69de29..6dddc35 100644 --- a/usdpl-back/src/rpc/mod.rs +++ b/usdpl-back/src/rpc/mod.rs @@ -0,0 +1,2 @@ +mod registry; +pub use registry::ServiceRegistry; diff --git a/usdpl-back/src/rpc/registry.rs b/usdpl-back/src/rpc/registry.rs new file mode 100644 index 0000000..6ae165d --- /dev/null +++ b/usdpl-back/src/rpc/registry.rs @@ -0,0 +1,42 @@ +use std::collections::HashMap; +use std::sync::Arc; +use async_lock::Mutex; + +use nrpc::{ServerService, ServiceError}; + +#[derive(Default, Clone)] +pub struct ServiceRegistry<'a> { + entries: HashMap>>>, +} + +impl<'a> ServiceRegistry<'a> { + /*pub async fn call(&self, package: &str, service: &str, method: &str, data: bytes::Bytes) -> Result { + let key = Self::descriptor(package, service); + self.call_descriptor(&key, method, data).await + } + + fn descriptor(package: &str, service: &str) -> String { + format!("{}.{}", package, service) + }*/ + + pub async fn call_descriptor(&self, descriptor: &str, method: &str, data: bytes::Bytes) -> Result { + if let Some(service) = self.entries.get(descriptor) { + let mut output = bytes::BytesMut::new(); + let mut service_lock = service.lock_arc().await; + service_lock.call(method, data, &mut output).await?; + Ok(output.into()) + } else { + Err(ServiceError::ServiceNotFound) + } + } + + pub fn register(&mut self, service: S) -> &mut Self { + let key = service.descriptor().to_owned(); + self.entries.insert(key, Arc::new(Mutex::new(Box::new(service)))); + self + } + + pub fn new() -> Self { + Self::default() + } +} diff --git a/usdpl-back/src/websockets.rs b/usdpl-back/src/websockets.rs new file mode 100644 index 0000000..9ad4fc0 --- /dev/null +++ b/usdpl-back/src/websockets.rs @@ -0,0 +1,108 @@ +use bytes::BytesMut; +use ratchet_rs::deflate::DeflateExtProvider; +use ratchet_rs::{Error as RatchetError, Message, ProtocolRegistry, WebSocketConfig}; +use tokio::net::{TcpListener, TcpStream}; + +use crate::rpc::ServiceRegistry; + +struct MethodDescriptor<'a> { + service: &'a str, + method: &'a str, +} + +/// Handler for communication to and from the front-end +pub struct WebsocketServer { + services: ServiceRegistry<'static>, + port: u16, +} + +impl WebsocketServer { + /// Initialise an instance of the back-end websocket server + pub fn new(port_usdpl: u16) -> Self { + Self { + services: ServiceRegistry::new(), + port: port_usdpl, + } + } + + /// Get the service registry that the server handles + pub fn registry(&mut self) -> &'_ mut ServiceRegistry<'static> { + &mut self.services + } + + /// Run the web server forever, asynchronously + pub async fn run(&self) -> std::io::Result<()> { + #[cfg(debug_assertions)] + let addr = (std::net::Ipv4Addr::UNSPECIFIED, self.port); + #[cfg(not(debug_assertions))] + let addr = (std::net::Ipv4Addr::LOCALHOST, self.port); + + let tcp = TcpListener::bind(addr).await?; + + while let Ok((stream, _addr_do_not_use)) = tcp.accept().await { + tokio::spawn(Self::connection_handler(self.services.clone(), stream)); + } + + Ok(()) + } + + async fn connection_handler(services: ServiceRegistry<'static>, stream: TcpStream) -> Result<(), RatchetError> { + let upgraded = ratchet_rs::accept_with( + stream, + WebSocketConfig::default(), + DeflateExtProvider::default(), + ProtocolRegistry::new(["usdpl-nrpc"])?, + ) + .await? + .upgrade() + .await?; + + let request_path = upgraded.request.uri().path(); + + let mut websocket = upgraded.websocket; + + let descriptor = Self::parse_uri_path(request_path) + .map_err(|e| RatchetError::with_cause(ratchet_rs::ErrorKind::Protocol, e))?; + + let mut buf = BytesMut::new(); + loop { + match websocket.read(&mut buf).await? { + Message::Text => return Err(RatchetError::with_cause(ratchet_rs::ErrorKind::Protocol, "Websocket text messages are not accepted")), + Message::Binary => { + let response = services.call_descriptor( + descriptor.service, + descriptor.method, + buf.clone().freeze() + ) + .await + .map_err(|e| RatchetError::with_cause(ratchet_rs::ErrorKind::Protocol, e.to_string()))?; + websocket.write_binary(response).await?; + }, + Message::Ping(x) => websocket.write_pong(x).await?, + Message::Pong(_) => {}, + Message::Close(_) => break, + } + } + Ok(()) + } + + fn parse_uri_path<'a>(path: &'a str) -> Result, &'static str> { + let mut iter = path.split('/'); + if let Some(service) = iter.next() { + if let Some(method) = iter.next() { + if iter.next().is_none() { + return Ok(MethodDescriptor { + service, + method + }); + } else { + Err("URL path has too many separators") + } + } else { + Err("URL path has no method") + } + } else { + Err("URL path has no service") + } + } +} diff --git a/usdpl-build/Cargo.toml b/usdpl-build/Cargo.toml index f2f4ab7..ff252ba 100644 --- a/usdpl-build/Cargo.toml +++ b/usdpl-build/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -nrpc-build = "0.5" +nrpc-build = "0.6" prost-build = "0.11" prost-types = "0.11" diff --git a/usdpl-front/Cargo.toml b/usdpl-front/Cargo.toml index eaea0ef..afffe0c 100644 --- a/usdpl-front/Cargo.toml +++ b/usdpl-front/Cargo.toml @@ -44,7 +44,7 @@ async-channel = "1.8" obfstr = { version = "0.3", optional = true } hex = { version = "0.4", optional = true } -nrpc = "0.2" +nrpc = "0.6" usdpl-core = { version = "0.11", path = "../usdpl-core" } prost = "0.11" diff --git a/usdpl-front/src/client_handler.rs b/usdpl-front/src/client_handler.rs index d123128..b592370 100644 --- a/usdpl-front/src/client_handler.rs +++ b/usdpl-front/src/client_handler.rs @@ -51,15 +51,17 @@ impl WebSocketHandler { #[async_trait::async_trait] impl ClientHandler for WebSocketHandler { async fn call(&mut self, + package: &str, service: &str, method: &str, input: bytes::Bytes, output: &mut bytes::BytesMut) -> Result<(), ServiceError> { let id = LAST_ID.fetch_add(1, Ordering::SeqCst); let url = format!( - "ws://usdpl-ws-{}.localhost:{}/{}/{}", + "ws://usdpl-ws-{}.localhost:{}/{}.{}/{}", id, self.port, + package, service, method, ); From febaafe50c6c993f6d1f3741b3759d9a12fc8ca3 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sun, 30 Apr 2023 15:28:59 -0400 Subject: [PATCH 03/12] Improve map type conversion for WASM interop --- Cargo.lock | 20 +- usdpl-back/Cargo.toml | 1 + usdpl-back/src/lib.rs | 12 + usdpl-back/src/websockets.rs | 15 ++ usdpl-build/Cargo.toml | 2 +- usdpl-build/src/back/mod.rs | 4 +- usdpl-build/src/front/mod.rs | 4 +- usdpl-build/src/front/service_generator.rs | 270 +++++++++++++++------ usdpl-build/src/lib.rs | 2 +- usdpl-build/src/proto_files.rs | 38 ++- 10 files changed, 273 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 00f5318..fa6fbce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -794,6 +794,15 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "nrpc" +version = "0.6.0" +dependencies = [ + "async-trait", + "bytes", + "prost", +] + [[package]] name = "nrpc" version = "0.6.0" @@ -807,11 +816,9 @@ dependencies = [ [[package]] name = "nrpc-build" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b598ecce0e6d4b2cb367143696174ae24bff5eb4aeb1d8eccffbfeef75fc68e" +version = "0.7.0" dependencies = [ - "nrpc", + "nrpc 0.6.0", "prettyplease 0.2.4", "proc-macro2", "prost-build", @@ -1495,8 +1502,9 @@ dependencies = [ "gettext-ng", "hex", "log", - "nrpc", + "nrpc 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "obfstr", + "prost", "ratchet_rs", "tokio", "usdpl-build", @@ -1535,7 +1543,7 @@ dependencies = [ "gloo-net", "hex", "js-sys", - "nrpc", + "nrpc 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "obfstr", "prost", "usdpl-build", diff --git a/usdpl-back/Cargo.toml b/usdpl-back/Cargo.toml index 26ba550..1037037 100644 --- a/usdpl-back/Cargo.toml +++ b/usdpl-back/Cargo.toml @@ -21,6 +21,7 @@ log = "0.4" # gRPC/protobuf nrpc = "0.6" async-lock = "2.7" +prost = "0.11" # websocket framework ratchet_rs = { version = "0.3", features = [ "deflate" ] } diff --git a/usdpl-back/src/lib.rs b/usdpl-back/src/lib.rs index fc4c052..cb2e8ce 100644 --- a/usdpl-back/src/lib.rs +++ b/usdpl-back/src/lib.rs @@ -43,3 +43,15 @@ pub mod api { pub mod core { pub use usdpl_core::*; } + +/// nrpc re-export +pub mod nrpc { + pub use nrpc::*; +} + +/// nRPC-generated exports +#[allow(missing_docs)] +#[allow(dead_code)] +pub mod services { + include!(concat!(env!("OUT_DIR"), "/mod.rs")); +} diff --git a/usdpl-back/src/websockets.rs b/usdpl-back/src/websockets.rs index 9ad4fc0..9ad22f9 100644 --- a/usdpl-back/src/websockets.rs +++ b/usdpl-back/src/websockets.rs @@ -30,6 +30,12 @@ impl WebsocketServer { &mut self.services } + /// Register a nRPC service for this server to handle + pub fn register(mut self, service: S) -> Self { + self.services.register(service); + self + } + /// Run the web server forever, asynchronously pub async fn run(&self) -> std::io::Result<()> { #[cfg(debug_assertions)] @@ -46,6 +52,15 @@ impl WebsocketServer { Ok(()) } + #[cfg(feature = "blocking")] + /// Run the server forever, blocking the current thread + pub fn run_blocking(self) -> std::io::Result<()> { + let runner = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build()?; + runner.block_on(self.run()) + } + async fn connection_handler(services: ServiceRegistry<'static>, stream: TcpStream) -> Result<(), RatchetError> { let upgraded = ratchet_rs::accept_with( stream, diff --git a/usdpl-build/Cargo.toml b/usdpl-build/Cargo.toml index ff252ba..9b1f1ad 100644 --- a/usdpl-build/Cargo.toml +++ b/usdpl-build/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -nrpc-build = "0.6" +nrpc-build = { version = "0.7", path = "../../nRPC/nrpc-build" } prost-build = "0.11" prost-types = "0.11" diff --git a/usdpl-build/src/back/mod.rs b/usdpl-build/src/back/mod.rs index 65d0120..21affc5 100644 --- a/usdpl-build/src/back/mod.rs +++ b/usdpl-build/src/back/mod.rs @@ -1,7 +1,7 @@ pub fn build() { crate::dump_protos_out().unwrap(); nrpc_build::compile_servers( - crate::all_proto_filenames().map(|n| crate::proto_out_path().clone().join(n)), - [crate::proto_out_path()] + crate::all_proto_filenames(crate::proto_builtins_out_path()), + crate::proto_out_paths() ) } diff --git a/usdpl-build/src/front/mod.rs b/usdpl-build/src/front/mod.rs index 698aa9d..37efa4b 100644 --- a/usdpl-build/src/front/mod.rs +++ b/usdpl-build/src/front/mod.rs @@ -11,8 +11,8 @@ pub fn build() { let shared_state = SharedState::new(); crate::dump_protos_out().unwrap(); nrpc_build::Transpiler::new( - crate::all_proto_filenames().map(|n| crate::proto_out_path().clone().join(n)), - [crate::proto_out_path()] + crate::all_proto_filenames(crate::proto_builtins_out_path()), + crate::proto_out_paths() ).unwrap() .generate_client() .with_preprocessor(nrpc_build::AbstractImpl::outer(WasmProtoPreprocessor::with_state(&shared_state))) diff --git a/usdpl-build/src/front/service_generator.rs b/usdpl-build/src/front/service_generator.rs index 173a757..9c53698 100644 --- a/usdpl-build/src/front/service_generator.rs +++ b/usdpl-build/src/front/service_generator.rs @@ -31,7 +31,7 @@ fn generate_service_methods(service: &Service, fds: &FileDescriptorSet) -> proc_ let mut params_to_fields = Vec::with_capacity(input_type.field.len()); for field in &input_type.field { //let param_name = quote::format_ident!("val{}", i.to_string()); - let type_name = translate_type(field, &service.name); + let type_name = ProtobufType::from_field(field, &service.name).to_tokens(); let field_name = quote::format_ident!("{}", field.name.as_ref().expect("Protobuf message field needs a name")); input_params.push(quote::quote!{ #field_name: #type_name, @@ -125,15 +125,6 @@ fn find_field<'a>(want_field: &str, descriptor: &'a DescriptorProto) -> Option<& None } -fn translate_type(field: &FieldDescriptorProto, service: &str) -> proc_macro2::TokenStream { - if let Some(type_name) = &field.type_name { - translate_type_name(type_name, service) - } else { - let number = field.r#type.unwrap(); - translate_type_known(number) - } -} - fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mut HashSet, handled_types: &mut HashSet, is_response_msg: bool, service: &str) -> proc_macro2::TokenStream { let msg_name = quote::format_ident!("{}{}", service, descriptor.name.as_ref().expect("Protobuf message needs a name")); let super_msg_name = quote::format_ident!("{}", descriptor.name.as_ref().expect("Protobuf message needs a name")); @@ -153,35 +144,107 @@ fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mu let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase()); let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase()); let key_field = find_field("key", descriptor).expect("Protobuf map entry has no key field"); - let key_type = translate_type(&key_field, service); + let key_type = ProtobufType::from_field(&key_field, service); let value_field = find_field("value", descriptor).expect("Protobuf map entry has no value field"); - let value_type = translate_type(&value_field, service); + let value_type = ProtobufType::from_field(&value_field, service); + + let key_type_tokens = key_type.to_tokens(); + let value_type_tokens = value_type.to_tokens(); + + let (fn_from, fn_into) = match (key_type, value_type) { + (ProtobufType::String, ProtobufType::String) => ( + quote::quote!{ + #[inline] + #[allow(dead_code)] + fn #special_fn_from(other: ::std::collections::HashMap<#key_type_tokens, #value_type_tokens>) -> #msg_name { + let map = #msg_name::new(); + for (key, val) in other.iter() { + map.set(&key.into(), &val.into()); + } + map + } + }, + quote::quote!{ + #[inline] + #[allow(dead_code)] + fn #special_fn_into(this: #msg_name) -> ::std::collections::HashMap<#key_type_tokens, #value_type_tokens> { + let mut output = ::std::collections::HashMap::<#key_type_tokens, #value_type_tokens>::new(); + this.for_each(&mut |key: ::wasm_bindgen::JsValue, val: ::wasm_bindgen::JsValue| { + if let Some(key) = key.as_string() { + if let Some(val) = val.as_string() { + output.insert(key, val); + } + } + }); + output + } + } + ), + (ProtobufType::String, ProtobufType::Double | ProtobufType::Float | ProtobufType::Int32| ProtobufType::Int64| ProtobufType::Uint32| ProtobufType::Uint64| ProtobufType::Sint32| ProtobufType::Sint64| ProtobufType::Fixed32| ProtobufType::Fixed64| ProtobufType::Sfixed32| ProtobufType::Sfixed64) => ( + quote::quote!{ + #[inline] + #[allow(dead_code)] + fn #special_fn_from(other: ::std::collections::HashMap<#key_type_tokens, #value_type_tokens>) -> #msg_name { + let map = #msg_name::new(); + for (key, val) in other.iter() { + map.set(&key.into(), &(val as f64).into()); + } + map + } + }, + quote::quote!{ + #[inline] + #[allow(dead_code)] + fn #special_fn_into(this: #msg_name) -> ::std::collections::HashMap<#key_type_tokens, #value_type_tokens> { + let mut output = ::std::collections::HashMap::<#key_type_tokens, #value_type_tokens>::new(); + this.for_each(&mut |key: ::wasm_bindgen::JsValue, val: ::wasm_bindgen::JsValue| { + if let Some(key) = key.as_string() { + if let Some(val) = val.as_f64() { + output.insert(key, val as _); + } + } + }); + output + } + } + ), + (ProtobufType::String, ProtobufType::Bool) => ( + quote::quote!{ + #[inline] + #[allow(dead_code)] + fn #special_fn_from(other: ::std::collections::HashMap<#key_type_tokens, #value_type_tokens>) -> #msg_name { + let map = #msg_name::new(); + for (key, val) in other.iter() { + map.set(&key.into(), &(val as f64).into()); + } + map + } + }, + quote::quote!{ + #[inline] + #[allow(dead_code)] + fn #special_fn_into(this: #msg_name) -> ::std::collections::HashMap<#key_type_tokens, #value_type_tokens> { + let mut output = ::std::collections::HashMap::<#key_type_tokens, #value_type_tokens>::new(); + this.for_each(&mut |key: ::wasm_bindgen::JsValue, val: ::wasm_bindgen::JsValue| { + if let Some(key) = key.as_string() { + if let Some(val) = val.as_bool() { + output.insert(key, val); + } + } + }); + output + } + } + ), + (key_type, value_type) => panic!("Unsupported map type map<{:?}, {:?}>", key_type, value_type), + }; + return quote::quote!{ pub type #msg_name = ::js_sys::Map; - #[inline] - #[allow(dead_code)] - fn #special_fn_from(other: ::std::collections::HashMap<#key_type, #value_type>) -> #msg_name { - let map = #msg_name::new(); - for (key, val) in other.iter() { - map.set(&key.into(), &val.into()); - } - map - } + #fn_from - #[inline] - #[allow(dead_code)] - fn #special_fn_into(this: #msg_name) -> ::std::collections::HashMap<#key_type, #value_type> { - let mut output = ::std::collections::HashMap::<#key_type, #value_type>::new(); - this.for_each(&mut |key: ::wasm_bindgen::JsValue, val: ::wasm_bindgen::JsValue| { - if let Some(key) = key.as_string() { - if let Some(val) = val.as_string() { - output.insert(key, val); - } - } - }); - output - } + #fn_into } } } else { @@ -207,7 +270,7 @@ fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mu if descriptor.field.len() == 1 { let field = &descriptor.field[0]; let field_name = quote::format_ident!("{}", field.name.as_ref().expect("Protobuf message field needs a name")); - let type_name = translate_type(field, service); + let type_name = ProtobufType::from_field(field, service).to_tokens(); gen_fields.push(quote::quote!{ pub #field_name: #type_name, }); @@ -267,7 +330,7 @@ fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mu } else { for field in &descriptor.field { let field_name = quote::format_ident!("{}", field.name.as_ref().expect("Protobuf message field needs a name")); - let type_name = translate_type(field, service); + let type_name = ProtobufType::from_field(field, service).to_tokens(); gen_fields.push(quote::quote!{ pub #field_name: #type_name, }); @@ -357,51 +420,100 @@ fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mu } -fn translate_type_name(name: &str, service: &str) -> proc_macro2::TokenStream { - match name { - "double" => quote::quote!{f64}, - "float" => quote::quote!{f32}, - "int32" => quote::quote!{i32}, - "int64" => quote::quote!{i64}, - "uint32" => quote::quote!{u32}, - "uint64" => quote::quote!{u64}, - "sint32" => quote::quote!{i32}, - "sint64" => quote::quote!{i64}, - "fixed32" => quote::quote!{u32}, - "fixed64" => quote::quote!{u64}, - "sfixed32" => quote::quote!{i32}, - "sfixed64" => quote::quote!{i64}, - "bool" => quote::quote!{bool}, - "string" => quote::quote!{String}, - "bytes" => quote::quote!{Vec}, - t => { - let ident = quote::format_ident!("{}{}", service, t.split('.').last().unwrap()); - quote::quote!{#ident} - }, - } +#[derive(Debug)] +enum ProtobufType { + Double, + Float, + Int32, + Int64, + Uint32, + Uint64, + Sint32, + Sint64, + Fixed32, + Fixed64, + Sfixed32, + Sfixed64, + Bool, + String, + Bytes, + Custom(String), } -fn translate_type_known(id: i32) -> proc_macro2::TokenStream { - match id { - //"double" => quote::quote!{f64}, - //"float" => quote::quote!{f32}, - //"int32" => quote::quote!{i32}, - //"int64" => quote::quote!{i64}, - //"uint32" => quote::quote!{u32}, - //"uint64" => quote::quote!{u64}, - //"sint32" => quote::quote!{i32}, - //"sint64" => quote::quote!{i64}, - //"fixed32" => quote::quote!{u32}, - //"fixed64" => quote::quote!{u64}, - //"sfixed32" => quote::quote!{i32}, - //"sfixed64" => quote::quote!{i64}, - //"bool" => quote::quote!{bool}, - 9 => quote::quote!{String}, - //"bytes" => quote::quote!{Vec}, - t => { - let ident = quote::format_ident!("UnknownType{}", t.to_string()); - quote::quote!{#ident} - }, +impl ProtobufType { + fn from_str(type_name: &str, service: &str) -> Self { + match type_name { + "double" => Self::Double, + "float" => Self::Float, + "int32" => Self::Int32, + "int64" => Self::Int64, + "uint32" => Self::Uint32, + "uint64" => Self::Uint64, + "sint32" => Self::Sint32, + "sint64" => Self::Sint64, + "fixed32" => Self::Fixed32, + "fixed64" => Self::Fixed64, + "sfixed32" => Self::Sfixed32, + "sfixed64" => Self::Sfixed64, + "bool" => Self::Bool, + "string" => Self::String, + "bytes" => Self::Bytes, + t => Self::Custom(format!("{}{}", service, t.split('.').last().unwrap())), + } + } + + fn from_id(id: i32) -> Self { + match id { + //"double" => quote::quote!{f64}, + //"float" => quote::quote!{f32}, + //"int32" => quote::quote!{i32}, + //"int64" => quote::quote!{i64}, + //"uint32" => quote::quote!{u32}, + //"uint64" => quote::quote!{u64}, + //"sint32" => quote::quote!{i32}, + //"sint64" => quote::quote!{i64}, + //"fixed32" => quote::quote!{u32}, + //"fixed64" => quote::quote!{u64}, + //"sfixed32" => quote::quote!{i32}, + //"sfixed64" => quote::quote!{i64}, + //"bool" => quote::quote!{bool}, + 9 => Self::String, + //"bytes" => quote::quote!{Vec}, + t => Self::Custom(format!("UnknownType{}", t)), + } + } + + fn from_field(field: &FieldDescriptorProto, service: &str) -> Self { + if let Some(type_name) = &field.type_name { + Self::from_str(type_name, service) + } else { + let number = field.r#type.unwrap(); + Self::from_id(number) + } + } + + fn to_tokens(&self) -> proc_macro2::TokenStream { + match self { + Self::Double => quote::quote!{f64}, + Self::Float => quote::quote!{f32}, + Self::Int32 => quote::quote!{i32}, + Self::Int64 => quote::quote!{i64}, + Self::Uint32 => quote::quote!{u32}, + Self::Uint64 => quote::quote!{u64}, + Self::Sint32 => quote::quote!{i32}, + Self::Sint64 => quote::quote!{i64}, + Self::Fixed32 => quote::quote!{u32}, + Self::Fixed64 => quote::quote!{u64}, + Self::Sfixed32 => quote::quote!{i32}, + Self::Sfixed64 => quote::quote!{i64}, + Self::Bool => quote::quote!{bool}, + Self::String => quote::quote!{String}, + Self::Bytes => quote::quote!{Vec}, + Self::Custom(t) => { + let ident = quote::format_ident!("{}", t); + quote::quote!{#ident} + }, + } } } diff --git a/usdpl-build/src/lib.rs b/usdpl-build/src/lib.rs index 31f742f..d543e54 100644 --- a/usdpl-build/src/lib.rs +++ b/usdpl-build/src/lib.rs @@ -2,4 +2,4 @@ pub mod back; pub mod front; mod proto_files; -pub use proto_files::{dump_protos, dump_protos_out, proto_out_path, all_proto_filenames}; +pub use proto_files::{dump_protos, dump_protos_out, proto_out_paths, all_proto_filenames, proto_builtins_out_path}; diff --git a/usdpl-build/src/proto_files.rs b/usdpl-build/src/proto_files.rs index 818c728..58e4bed 100644 --- a/usdpl-build/src/proto_files.rs +++ b/usdpl-build/src/proto_files.rs @@ -5,6 +5,8 @@ struct IncludedFileStr<'a> { contents: &'a str, } +const ADDITIONAL_PROTOBUFS_ENV_VAR: &'static str = "USDPL_PROTOS_PATH"; + const DEBUG_PROTO: IncludedFileStr<'static> = IncludedFileStr { filename: "debug.proto", contents: include_str!("../protos/debug.proto"), @@ -20,12 +22,40 @@ const ALL_PROTOS: [IncludedFileStr<'static>; 2] = [ TRANSLATIONS_PROTO, ]; -pub fn proto_out_path() -> PathBuf { +pub fn proto_builtins_out_path() -> PathBuf { PathBuf::from(std::env::var("OUT_DIR").expect("Not in a build.rs context (missing $OUT_DIR)")).join("protos") } -pub fn all_proto_filenames() -> impl Iterator { - ALL_PROTOS.iter().map(|x| x.filename) +pub fn proto_out_paths() -> impl Iterator { + std::iter::once(proto_builtins_out_path()) + .map(|x| x.to_str().unwrap().to_owned()) + .chain(custom_protos_dirs().into_iter()) +} + +fn custom_protos_dirs() -> Vec { + let dirs = std::env::var(ADDITIONAL_PROTOBUFS_ENV_VAR).unwrap_or_else(|_| "".to_owned()); + dirs.split(':') + .filter(|x| std::fs::read_dir(x).is_ok()) + .map(|x| x.to_owned()) + .collect() +} + +fn custom_protos_filenames() -> Vec { + let dirs = std::env::var(ADDITIONAL_PROTOBUFS_ENV_VAR).unwrap_or_else(|_| "".to_owned()); + dirs.split(':') + .map(std::fs::read_dir) + .filter(|x| x.is_ok()) + .flat_map(|x| x.unwrap()) + .filter(|x| x.is_ok()) + .map(|x| x.unwrap().path()) + .filter(|x| if let Some(ext) = x.extension() { ext.to_ascii_lowercase() == "proto" && x.is_file() } else { false }) + .filter_map(|x| x.to_str().map(|x| x.to_owned())) + .collect() +} + +pub fn all_proto_filenames(p: impl AsRef + 'static) -> impl Iterator { + //let p = p.as_ref(); + ALL_PROTOS.iter().map(move |x| p.as_ref().join(x.filename).to_str().unwrap().to_owned()).chain(custom_protos_filenames()) } pub fn dump_protos(p: impl AsRef) -> std::io::Result<()> { @@ -38,7 +68,7 @@ pub fn dump_protos(p: impl AsRef) -> std::io::Result<()> { } pub fn dump_protos_out() -> std::io::Result<()> { - let path = proto_out_path(); + let path = proto_builtins_out_path(); std::fs::create_dir_all(&path)?; dump_protos(&path) } From 0b44ebc12bc30ed3c6b9b46dc95e26317a21600e Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sun, 4 Jun 2023 14:05:33 -0400 Subject: [PATCH 04/12] Improve flexibility of WASM conversions in code gen --- Cargo.lock | 1 - usdpl-back/build.rs | 3 - usdpl-back/src/api_any/dirs.rs | 8 +- usdpl-back/src/api_common/dirs.rs | 10 +- usdpl-back/src/api_common/files.rs | 10 +- usdpl-back/src/lib.rs | 26 +- usdpl-back/src/rpc/registry.rs | 12 +- usdpl-back/src/websockets.rs | 42 +- usdpl-build/protos/debug.proto | 4 +- usdpl-build/src/back/mod.rs | 9 +- usdpl-build/src/front/mod.rs | 26 +- usdpl-build/src/front/preprocessor.rs | 7 +- usdpl-build/src/front/service_generator.rs | 805 ++++++++++++--------- usdpl-build/src/front/shared_state.rs | 4 +- usdpl-build/src/lib.rs | 4 +- usdpl-build/src/proto_files.rs | 34 +- usdpl-core/src/lib.rs | 12 +- usdpl-core/src/remote_call.rs | 7 +- usdpl-core/src/serdes/dump_impl.rs | 27 +- usdpl-core/src/serdes/load_impl.rs | 24 +- usdpl-core/src/serdes/primitive.rs | 7 +- usdpl-core/src/serdes/traits.rs | 20 +- usdpl-core/src/socket.rs | 40 +- usdpl-front/Cargo.toml | 3 - usdpl-front/build.rs | 3 - usdpl-front/src/client_handler.rs | 45 +- usdpl-front/src/connection.rs | 62 +- usdpl-front/src/lib.rs | 38 +- usdpl-front/src/wasm/arrays.rs | 94 +++ usdpl-front/src/wasm/maps.rs | 99 +++ usdpl-front/src/wasm/mod.rs | 7 + usdpl-front/src/wasm/trivials.rs | 40 + usdpl-front/src/wasm/wasm_traits.rs | 40 + 33 files changed, 1028 insertions(+), 545 deletions(-) delete mode 100644 usdpl-back/build.rs delete mode 100644 usdpl-front/build.rs create mode 100644 usdpl-front/src/wasm/arrays.rs create mode 100644 usdpl-front/src/wasm/maps.rs create mode 100644 usdpl-front/src/wasm/mod.rs create mode 100644 usdpl-front/src/wasm/trivials.rs create mode 100644 usdpl-front/src/wasm/wasm_traits.rs diff --git a/Cargo.lock b/Cargo.lock index fa6fbce..e827963 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1546,7 +1546,6 @@ dependencies = [ "nrpc 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "obfstr", "prost", - "usdpl-build", "usdpl-core", "wasm-bindgen", "wasm-bindgen-futures", diff --git a/usdpl-back/build.rs b/usdpl-back/build.rs deleted file mode 100644 index 36803b5..0000000 --- a/usdpl-back/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - usdpl_build::back::build() -} diff --git a/usdpl-back/src/api_any/dirs.rs b/usdpl-back/src/api_any/dirs.rs index 6ac1b5a..e35ba03 100644 --- a/usdpl-back/src/api_any/dirs.rs +++ b/usdpl-back/src/api_any/dirs.rs @@ -5,14 +5,10 @@ use std::process::Command; /// The home directory of the user currently running the Steam Deck UI (specifically: running gamescope). pub fn home() -> Option { - let who_out = Command::new("who") - .output().ok()?; + let who_out = Command::new("who").output().ok()?; let who_str = String::from_utf8_lossy(who_out.stdout.as_slice()); for login in who_str.split("\n") { - let username = login - .split(" ") - .next()? - .trim(); + let username = login.split(" ").next()?.trim(); let path = Path::new("/home").join(username); if path.is_dir() { return Some(path); diff --git a/usdpl-back/src/api_common/dirs.rs b/usdpl-back/src/api_common/dirs.rs index 033aee6..c27e12a 100644 --- a/usdpl-back/src/api_common/dirs.rs +++ b/usdpl-back/src/api_common/dirs.rs @@ -7,12 +7,10 @@ pub fn home() -> Option { #[cfg(not(any(feature = "decky", feature = "crankshaft")))] let result = crate::api_any::dirs::home(); #[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))] - let result = crate::api_decky::home().ok() - .map(|x| PathBuf::from(x) - .join("..") - .canonicalize() - .ok() - ).flatten(); + let result = crate::api_decky::home() + .ok() + .map(|x| PathBuf::from(x).join("..").canonicalize().ok()) + .flatten(); result } diff --git a/usdpl-back/src/api_common/files.rs b/usdpl-back/src/api_common/files.rs index 48b555f..f3085a3 100644 --- a/usdpl-back/src/api_common/files.rs +++ b/usdpl-back/src/api_common/files.rs @@ -1,8 +1,8 @@ //! Common low-level file operations use std::fmt::Display; -use std::path::Path; use std::fs::File; -use std::io::{Read, Write, self}; +use std::io::{self, Read, Write}; +use std::path::Path; use std::str::FromStr; /// Write something to a file. @@ -31,14 +31,12 @@ impl std::fmt::Display for ReadError { } } -impl std::error::Error for ReadError { - -} +impl std::error::Error for ReadError {} /// Read something from a file. /// Useful for kernel configuration files. #[inline] -pub fn read_single, D: FromStr, E>(path: P) -> Result> { +pub fn read_single, D: FromStr, E>(path: P) -> Result> { let mut file = File::open(path).map_err(ReadError::Io)?; let mut string = String::new(); file.read_to_string(&mut string).map_err(ReadError::Io)?; diff --git a/usdpl-back/src/lib.rs b/usdpl-back/src/lib.rs index cb2e8ce..769b0a6 100644 --- a/usdpl-back/src/lib.rs +++ b/usdpl-back/src/lib.rs @@ -5,12 +5,10 @@ //! #![warn(missing_docs)] -#[cfg(not(any(feature = "decky", feature = "crankshaft")))] +#[cfg(not(any(feature = "decky")))] mod api_any; mod api_common; -#[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))] -mod api_crankshaft; -#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))] +#[cfg(all(feature = "decky", not(any(feature = "any"))))] mod api_decky; mod rpc; @@ -27,16 +25,16 @@ pub mod api { pub use super::api_common::*; /// Standard interfaces not specific to a single plugin loader - #[cfg(not(any(feature = "decky", feature = "crankshaft")))] - pub mod any { pub use super::super::api_any::*; } - - /// Crankshaft-specific interfaces (FIXME) - #[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))] - pub mod crankshaft { pub use super::super::api_crankshaft::*; } + #[cfg(not(any(feature = "decky")))] + pub mod any { + pub use super::super::api_any::*; + } /// Decky-specific interfaces - #[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))] - pub mod decky { pub use super::super::api_decky::*; } + #[cfg(all(feature = "decky", not(any(feature = "any"))))] + pub mod decky { + pub use super::super::api_decky::*; + } } /// usdpl-core re-export @@ -49,9 +47,9 @@ pub mod nrpc { pub use nrpc::*; } -/// nRPC-generated exports +/*/// nRPC-generated exports #[allow(missing_docs)] #[allow(dead_code)] pub mod services { include!(concat!(env!("OUT_DIR"), "/mod.rs")); -} +}*/ diff --git a/usdpl-back/src/rpc/registry.rs b/usdpl-back/src/rpc/registry.rs index 6ae165d..0560696 100644 --- a/usdpl-back/src/rpc/registry.rs +++ b/usdpl-back/src/rpc/registry.rs @@ -1,6 +1,6 @@ +use async_lock::Mutex; use std::collections::HashMap; use std::sync::Arc; -use async_lock::Mutex; use nrpc::{ServerService, ServiceError}; @@ -19,7 +19,12 @@ impl<'a> ServiceRegistry<'a> { format!("{}.{}", package, service) }*/ - pub async fn call_descriptor(&self, descriptor: &str, method: &str, data: bytes::Bytes) -> Result { + pub async fn call_descriptor( + &self, + descriptor: &str, + method: &str, + data: bytes::Bytes, + ) -> Result { if let Some(service) = self.entries.get(descriptor) { let mut output = bytes::BytesMut::new(); let mut service_lock = service.lock_arc().await; @@ -32,7 +37,8 @@ impl<'a> ServiceRegistry<'a> { pub fn register(&mut self, service: S) -> &mut Self { let key = service.descriptor().to_owned(); - self.entries.insert(key, Arc::new(Mutex::new(Box::new(service)))); + self.entries + .insert(key, Arc::new(Mutex::new(Box::new(service)))); self } diff --git a/usdpl-back/src/websockets.rs b/usdpl-back/src/websockets.rs index 9ad22f9..5fbed68 100644 --- a/usdpl-back/src/websockets.rs +++ b/usdpl-back/src/websockets.rs @@ -61,16 +61,19 @@ impl WebsocketServer { runner.block_on(self.run()) } - async fn connection_handler(services: ServiceRegistry<'static>, stream: TcpStream) -> Result<(), RatchetError> { + async fn connection_handler( + services: ServiceRegistry<'static>, + stream: TcpStream, + ) -> Result<(), RatchetError> { let upgraded = ratchet_rs::accept_with( stream, WebSocketConfig::default(), DeflateExtProvider::default(), ProtocolRegistry::new(["usdpl-nrpc"])?, ) - .await? - .upgrade() - .await?; + .await? + .upgrade() + .await?; let request_path = upgraded.request.uri().path(); @@ -82,19 +85,27 @@ impl WebsocketServer { let mut buf = BytesMut::new(); loop { match websocket.read(&mut buf).await? { - Message::Text => return Err(RatchetError::with_cause(ratchet_rs::ErrorKind::Protocol, "Websocket text messages are not accepted")), + Message::Text => { + return Err(RatchetError::with_cause( + ratchet_rs::ErrorKind::Protocol, + "Websocket text messages are not accepted", + )) + } Message::Binary => { - let response = services.call_descriptor( - descriptor.service, - descriptor.method, - buf.clone().freeze() - ) + let response = services + .call_descriptor( + descriptor.service, + descriptor.method, + buf.clone().freeze(), + ) .await - .map_err(|e| RatchetError::with_cause(ratchet_rs::ErrorKind::Protocol, e.to_string()))?; + .map_err(|e| { + RatchetError::with_cause(ratchet_rs::ErrorKind::Protocol, e.to_string()) + })?; websocket.write_binary(response).await?; - }, + } Message::Ping(x) => websocket.write_pong(x).await?, - Message::Pong(_) => {}, + Message::Pong(_) => {} Message::Close(_) => break, } } @@ -106,10 +117,7 @@ impl WebsocketServer { if let Some(service) = iter.next() { if let Some(method) = iter.next() { if iter.next().is_none() { - return Ok(MethodDescriptor { - service, - method - }); + return Ok(MethodDescriptor { service, method }); } else { Err("URL path has too many separators") } diff --git a/usdpl-build/protos/debug.proto b/usdpl-build/protos/debug.proto index c5b7168..86aaf0b 100644 --- a/usdpl-build/protos/debug.proto +++ b/usdpl-build/protos/debug.proto @@ -22,4 +22,6 @@ message LogMessage { string msg = 2; } -message Empty {} +message Empty { + bool ok = 1; +} diff --git a/usdpl-build/src/back/mod.rs b/usdpl-build/src/back/mod.rs index 21affc5..ec1b904 100644 --- a/usdpl-build/src/back/mod.rs +++ b/usdpl-build/src/back/mod.rs @@ -1,7 +1,10 @@ -pub fn build() { +pub fn build( + custom_protos: impl Iterator, + custom_dirs: impl Iterator, +) { crate::dump_protos_out().unwrap(); nrpc_build::compile_servers( - crate::all_proto_filenames(crate::proto_builtins_out_path()), - crate::proto_out_paths() + crate::all_proto_filenames(crate::proto_builtins_out_path(), custom_protos), + crate::proto_out_paths(custom_dirs), ) } diff --git a/usdpl-build/src/front/mod.rs b/usdpl-build/src/front/mod.rs index 37efa4b..b9a6f29 100644 --- a/usdpl-build/src/front/mod.rs +++ b/usdpl-build/src/front/mod.rs @@ -7,16 +7,24 @@ pub use service_generator::WasmServiceGenerator; mod shared_state; pub(crate) use shared_state::SharedState; -pub fn build() { +pub fn build( + custom_protos: impl Iterator, + custom_dirs: impl Iterator, +) { let shared_state = SharedState::new(); crate::dump_protos_out().unwrap(); nrpc_build::Transpiler::new( - crate::all_proto_filenames(crate::proto_builtins_out_path()), - crate::proto_out_paths() - ).unwrap() - .generate_client() - .with_preprocessor(nrpc_build::AbstractImpl::outer(WasmProtoPreprocessor::with_state(&shared_state))) - .with_service_generator(nrpc_build::AbstractImpl::outer(WasmServiceGenerator::with_state(&shared_state))) - .transpile() - .unwrap() + crate::all_proto_filenames(crate::proto_builtins_out_path(), custom_protos), + crate::proto_out_paths(custom_dirs), + ) + .unwrap() + .generate_client() + .with_preprocessor(nrpc_build::AbstractImpl::outer( + WasmProtoPreprocessor::with_state(&shared_state), + )) + .with_service_generator(nrpc_build::AbstractImpl::outer( + WasmServiceGenerator::with_state(&shared_state), + )) + .transpile() + .unwrap() } diff --git a/usdpl-build/src/front/preprocessor.rs b/usdpl-build/src/front/preprocessor.rs index dd3b4a2..bcaf763 100644 --- a/usdpl-build/src/front/preprocessor.rs +++ b/usdpl-build/src/front/preprocessor.rs @@ -18,10 +18,7 @@ impl WasmProtoPreprocessor { impl IPreprocessor for WasmProtoPreprocessor { fn process(&mut self, fds: &mut FileDescriptorSet) -> proc_macro2::TokenStream { - self.shared.lock() - .expect("Cannot lock shared state") - .fds = Some(fds.clone()); - quote::quote!{} + self.shared.lock().expect("Cannot lock shared state").fds = Some(fds.clone()); + quote::quote! {} } } - diff --git a/usdpl-build/src/front/service_generator.rs b/usdpl-build/src/front/service_generator.rs index 9c53698..584fc45 100644 --- a/usdpl-build/src/front/service_generator.rs +++ b/usdpl-build/src/front/service_generator.rs @@ -1,8 +1,8 @@ use std::collections::HashSet; -use prost_build::Service; -use prost_types::{FileDescriptorSet, DescriptorProto, EnumDescriptorProto, FieldDescriptorProto}; use nrpc_build::IServiceGenerator; +use prost_build::Service; +use prost_types::{DescriptorProto, EnumDescriptorProto, FieldDescriptorProto, FileDescriptorSet}; use super::SharedState; @@ -18,79 +18,102 @@ impl WasmServiceGenerator { } } -fn generate_service_methods(service: &Service, fds: &FileDescriptorSet) -> proc_macro2::TokenStream { +fn generate_service_methods( + service: &Service, + fds: &FileDescriptorSet, +) -> proc_macro2::TokenStream { let mut gen_methods = Vec::with_capacity(service.methods.len()); for method in &service.methods { let method_name = quote::format_ident!("{}", method.name); let method_input = quote::format_ident!("{}{}", &service.name, method.input_type); - let method_output = quote::format_ident!("{}{}", &service.name, method.output_type); + let method_output = quote::format_ident!("{}{}Wasm", &service.name, method.output_type); + let method_output_as_in = quote::format_ident!("{}{}", &service.name, method.output_type); - let input_type = find_message_type(&method.input_type, &service.package, fds).expect("Protobuf message is used but not found"); + let input_type = find_message_type(&method.input_type, &service.package, fds) + .expect("Protobuf message is used but not found"); let mut input_params = Vec::with_capacity(input_type.field.len()); let mut params_to_fields = Vec::with_capacity(input_type.field.len()); for field in &input_type.field { //let param_name = quote::format_ident!("val{}", i.to_string()); - let type_name = ProtobufType::from_field(field, &service.name).to_tokens(); - let field_name = quote::format_ident!("{}", field.name.as_ref().expect("Protobuf message field needs a name")); - input_params.push(quote::quote!{ - #field_name: #type_name, + let type_enum = ProtobufType::from_field(field, &service.name, false); + //let rs_type_name = type_enum.to_tokens(); + let js_type_name = type_enum.to_wasm_tokens(); + let rs_type_name = type_enum.to_tokens(); + let field_name = quote::format_ident!( + "{}", + field + .name + .as_ref() + .expect("Protobuf message field needs a name") + ); + input_params.push(quote::quote! { + #field_name: #js_type_name, }); - params_to_fields.push(quote::quote!{ - #field_name,//: #field_name, + params_to_fields.push(quote::quote! { + #field_name: #rs_type_name::from_wasm(#field_name.into()),//: #field_name, }); } let params_to_fields_transformer = if input_type.field.len() == 1 { - let field_name = quote::format_ident!("{}", input_type.field[0].name.as_ref().expect("Protobuf message field needs a name")); - quote::quote!{ - let val = #field_name; + let field_name = quote::format_ident!( + "{}", + input_type.field[0] + .name + .as_ref() + .expect("Protobuf message field needs a name") + ); + quote::quote! { + let val = #method_input::from_wasm(#field_name.into()); } } else if input_type.field.is_empty() { - quote::quote!{ + quote::quote! { let val = #method_input {}; } } else { - quote::quote!{ + quote::quote! { let val = #method_input { #(#params_to_fields)* }; } }; - let special_fn_into_input = quote::format_ident!("{}_convert_into", method.input_type.split('.').last().unwrap().to_lowercase()); + gen_methods.push(quote::quote! { + #[wasm_bindgen] + pub async fn #method_name(&mut self, #(#input_params)*) -> Option<#method_output> { - let special_fn_from_output = quote::format_ident!("{}_convert_from", method.output_type.split('.').last().unwrap().to_lowercase()); + #params_to_fields_transformer - gen_methods.push( - quote::quote!{ - #[wasm_bindgen] - pub async fn #method_name(&mut self, #(#input_params)*) -> Option<#method_output> { - - #params_to_fields_transformer - - match self.service.#method_name(#special_fn_into_input(val)).await { - Ok(x) => Some(#special_fn_from_output(x)), - Err(_e) => { - // TODO log error - None - } + match self.service.#method_name(val.into()).await { + Ok(x) => { + let x2: #method_output_as_in = x.into(); + Some(x2.into_wasm()) + }, + Err(_e) => { + // TODO log error + None } } } - ); + }); } - quote::quote!{ + quote::quote! { #(#gen_methods)* } } -fn find_message_type<'a>(want_type: &str, want_package: &str, fds: &'a FileDescriptorSet) -> Option<&'a DescriptorProto> { +fn find_message_type<'a>( + want_type: &str, + want_package: &str, + fds: &'a FileDescriptorSet, +) -> Option<&'a DescriptorProto> { for file in &fds.file { - for message_type in &file.message_type { - if let Some(name) = &message_type.name { - if let Some(pkg) = &file.package { - if name == want_type && pkg == want_package { - return Some(message_type); + if let Some(pkg) = &file.package { + if pkg == want_package { + for message_type in &file.message_type { + if let Some(name) = &message_type.name { + if name == want_type { + return Some(message_type); + } } } } @@ -99,7 +122,11 @@ fn find_message_type<'a>(want_type: &str, want_package: &str, fds: &'a FileDescr None } -fn find_enum_type<'a>(want_type: &str, want_package: &str, fds: &'a FileDescriptorSet) -> Option<&'a EnumDescriptorProto> { +fn find_enum_type<'a>( + want_type: &str, + want_package: &str, + fds: &'a FileDescriptorSet, +) -> Option<&'a EnumDescriptorProto> { for file in &fds.file { for enum_type in &file.enum_type { if let Some(name) = &enum_type.name { @@ -114,7 +141,10 @@ fn find_enum_type<'a>(want_type: &str, want_package: &str, fds: &'a FileDescript None } -fn find_field<'a>(want_field: &str, descriptor: &'a DescriptorProto) -> Option<&'a FieldDescriptorProto> { +fn find_field<'a>( + want_field: &str, + descriptor: &'a DescriptorProto, +) -> Option<&'a FieldDescriptorProto> { for field in &descriptor.field { if let Some(name) = &field.name { if name == want_field { @@ -125,9 +155,47 @@ fn find_field<'a>(want_field: &str, descriptor: &'a DescriptorProto) -> Option<& None } -fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mut HashSet, handled_types: &mut HashSet, is_response_msg: bool, service: &str) -> proc_macro2::TokenStream { - let msg_name = quote::format_ident!("{}{}", service, descriptor.name.as_ref().expect("Protobuf message needs a name")); - let super_msg_name = quote::format_ident!("{}", descriptor.name.as_ref().expect("Protobuf message needs a name")); +fn is_known_map(field: &FieldDescriptorProto, known_maps: &HashSet) -> bool { + if let Some(type_name) = &field.type_name { + let name = type_name.split('.').last().unwrap(); + known_maps.contains(name) + } else { + false + } +} + +fn generate_wasm_struct_interop( + descriptor: &DescriptorProto, + handled_enums: &mut HashSet, + handled_types: &mut HashSet, + known_maps: &mut HashSet, + seen_super_enums: &mut HashSet, + is_response_msg: bool, + service: &str, +) -> proc_macro2::TokenStream { + let msg_name = quote::format_ident!( + "{}{}", + service, + descriptor + .name + .as_ref() + .expect("Protobuf message needs a name") + ); + let msg_name_wasm = quote::format_ident!( + "{}{}Wasm", + service, + descriptor + .name + .as_ref() + .expect("Protobuf message needs a name") + ); + let super_msg_name = quote::format_ident!( + "{}", + descriptor + .name + .as_ref() + .expect("Protobuf message needs a name") + ); let mut gen_fields = Vec::with_capacity(descriptor.field.len()); let mut gen_into_fields = Vec::with_capacity(descriptor.field.len()); let mut gen_from_fields = Vec::with_capacity(descriptor.field.len()); @@ -137,126 +205,60 @@ fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mu let mut gen_enums = Vec::with_capacity(descriptor.enum_type.len()); if let Some(options) = &descriptor.options { + //dbg!(options); if let Some(map_entry) = options.map_entry { // TODO deal with options when necessary if map_entry { - let name = descriptor.name.clone().expect("Protobuf message needs a name"); - let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase()); - let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase()); - let key_field = find_field("key", descriptor).expect("Protobuf map entry has no key field"); - let key_type = ProtobufType::from_field(&key_field, service); - let value_field = find_field("value", descriptor).expect("Protobuf map entry has no value field"); - let value_type = ProtobufType::from_field(&value_field, service); + //dbg!(descriptor); + let name = descriptor + .name + .clone() + .expect("Protobuf message needs a name"); + known_maps.insert(name.clone()); + let key_field = + find_field("key", descriptor).expect("Protobuf map entry has no key field"); + let key_type = ProtobufType::from_field(&key_field, service, false); + let value_field = + find_field("value", descriptor).expect("Protobuf map entry has no value field"); + let value_type = ProtobufType::from_field(&value_field, service, false); - let key_type_tokens = key_type.to_tokens(); - let value_type_tokens = value_type.to_tokens(); - - let (fn_from, fn_into) = match (key_type, value_type) { - (ProtobufType::String, ProtobufType::String) => ( - quote::quote!{ - #[inline] - #[allow(dead_code)] - fn #special_fn_from(other: ::std::collections::HashMap<#key_type_tokens, #value_type_tokens>) -> #msg_name { - let map = #msg_name::new(); - for (key, val) in other.iter() { - map.set(&key.into(), &val.into()); - } - map - } - }, - quote::quote!{ - #[inline] - #[allow(dead_code)] - fn #special_fn_into(this: #msg_name) -> ::std::collections::HashMap<#key_type_tokens, #value_type_tokens> { - let mut output = ::std::collections::HashMap::<#key_type_tokens, #value_type_tokens>::new(); - this.for_each(&mut |key: ::wasm_bindgen::JsValue, val: ::wasm_bindgen::JsValue| { - if let Some(key) = key.as_string() { - if let Some(val) = val.as_string() { - output.insert(key, val); - } - } - }); - output - } - } - ), - (ProtobufType::String, ProtobufType::Double | ProtobufType::Float | ProtobufType::Int32| ProtobufType::Int64| ProtobufType::Uint32| ProtobufType::Uint64| ProtobufType::Sint32| ProtobufType::Sint64| ProtobufType::Fixed32| ProtobufType::Fixed64| ProtobufType::Sfixed32| ProtobufType::Sfixed64) => ( - quote::quote!{ - #[inline] - #[allow(dead_code)] - fn #special_fn_from(other: ::std::collections::HashMap<#key_type_tokens, #value_type_tokens>) -> #msg_name { - let map = #msg_name::new(); - for (key, val) in other.iter() { - map.set(&key.into(), &(val as f64).into()); - } - map - } - }, - quote::quote!{ - #[inline] - #[allow(dead_code)] - fn #special_fn_into(this: #msg_name) -> ::std::collections::HashMap<#key_type_tokens, #value_type_tokens> { - let mut output = ::std::collections::HashMap::<#key_type_tokens, #value_type_tokens>::new(); - this.for_each(&mut |key: ::wasm_bindgen::JsValue, val: ::wasm_bindgen::JsValue| { - if let Some(key) = key.as_string() { - if let Some(val) = val.as_f64() { - output.insert(key, val as _); - } - } - }); - output - } - } - ), - (ProtobufType::String, ProtobufType::Bool) => ( - quote::quote!{ - #[inline] - #[allow(dead_code)] - fn #special_fn_from(other: ::std::collections::HashMap<#key_type_tokens, #value_type_tokens>) -> #msg_name { - let map = #msg_name::new(); - for (key, val) in other.iter() { - map.set(&key.into(), &(val as f64).into()); - } - map - } - }, - quote::quote!{ - #[inline] - #[allow(dead_code)] - fn #special_fn_into(this: #msg_name) -> ::std::collections::HashMap<#key_type_tokens, #value_type_tokens> { - let mut output = ::std::collections::HashMap::<#key_type_tokens, #value_type_tokens>::new(); - this.for_each(&mut |key: ::wasm_bindgen::JsValue, val: ::wasm_bindgen::JsValue| { - if let Some(key) = key.as_string() { - if let Some(val) = val.as_bool() { - output.insert(key, val); - } - } - }); - output - } - } - ), - (key_type, value_type) => panic!("Unsupported map type map<{:?}, {:?}>", key_type, value_type), + let map_type = ProtobufType::Map { + key: Box::new(key_type), + value: Box::new(value_type), }; - return quote::quote!{ - pub type #msg_name = ::js_sys::Map; + //dbg!("Generated map type", name); - #fn_from + let map_tokens = map_type.to_tokens(); + let wasm_tokens = map_type.to_wasm_tokens(); - #fn_into - } + return quote::quote! { + pub type #msg_name = #map_tokens; + pub type #msg_name_wasm = #wasm_tokens; + }; } - } else { - todo!("Deal with message options when necessary"); } + // TODO Deal with other message options when necessary } + //dbg!(&descriptor.options); + for n_type in &descriptor.nested_type { - let type_name = n_type.name.clone().expect("Protobuf nested message needs a name"); + let type_name = n_type + .name + .clone() + .expect("Protobuf nested message needs a name"); if !handled_types.contains(&type_name) { handled_types.insert(type_name); - gen_nested_types.push(generate_wasm_struct_interop(n_type, handled_enums, handled_types, is_response_msg, service)); + gen_nested_types.push(generate_wasm_struct_interop( + n_type, + handled_enums, + handled_types, + known_maps, + seen_super_enums, + is_response_msg, + service, + )); } } @@ -264,62 +266,55 @@ fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mu let type_name = e_type.name.clone().expect("Protobuf enum needs a name"); if !handled_enums.contains(&type_name) { handled_enums.insert(type_name); - gen_enums.push(generate_wasm_enum_interop(e_type, service)); + gen_enums.push(generate_wasm_enum_interop( + e_type, + service, + seen_super_enums, + )); } } - if descriptor.field.len() == 1 { - let field = &descriptor.field[0]; - let field_name = quote::format_ident!("{}", field.name.as_ref().expect("Protobuf message field needs a name")); - let type_name = ProtobufType::from_field(field, service).to_tokens(); - gen_fields.push(quote::quote!{ - pub #field_name: #type_name, - }); - if let Some(name) = &field.type_name { - let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase()); - let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase()); - gen_into_fields.push( - quote::quote!{ - #field_name: #special_fn_into(this) - } - ); - gen_from_fields.push( - quote::quote!{ - #special_fn_from(other.#field_name) - } - ); - } else { - gen_into_fields.push( - quote::quote!{ - #field_name: this - } - ); + if descriptor.field.len() == 0 { + quote::quote! { + pub type #msg_name = (); + pub type #msg_name_wasm = #msg_name; - gen_from_fields.push( - quote::quote!{ - other.#field_name - } - ); + #(#gen_nested_types)* + + #(#gen_enums)* } + } else if descriptor.field.len() == 1 { + let field = &descriptor.field[0]; + //dbg!(descriptor, field); + let field_name = quote::format_ident!( + "{}", + field + .name + .as_ref() + .expect("Protobuf message field needs a name") + ); + let type_enum = ProtobufType::from_field(field, service, is_known_map(field, known_maps)); + let type_name = type_enum.to_tokens(); + let wasm_type_name = type_enum.to_wasm_tokens(); - let name = descriptor.name.clone().expect("Protobuf message needs a name"); - let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase()); - let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase()); - - quote::quote!{ + quote::quote! { pub type #msg_name = #type_name; + pub type #msg_name_wasm = #wasm_type_name; - #[inline] - #[allow(dead_code)] - fn #special_fn_from(other: super::#super_msg_name) -> #msg_name { - #(#gen_from_fields)* + impl std::convert::Into for #msg_name { + #[inline] + fn into(self) -> super::#super_msg_name { + super::#super_msg_name { + #field_name: self + } + } } - #[inline] - #[allow(dead_code)] - fn #special_fn_into(this: #msg_name) -> super::#super_msg_name { - super::#super_msg_name { - #(#gen_into_fields)* + impl std::convert::From for #msg_name { + #[inline] + #[allow(unused_variables)] + fn from(other: super::#super_msg_name) -> Self { + other.#field_name } } @@ -329,58 +324,60 @@ fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mu } } else { for field in &descriptor.field { - let field_name = quote::format_ident!("{}", field.name.as_ref().expect("Protobuf message field needs a name")); - let type_name = ProtobufType::from_field(field, service).to_tokens(); - gen_fields.push(quote::quote!{ + let field_name = quote::format_ident!( + "{}", + field + .name + .as_ref() + .expect("Protobuf message field needs a name") + ); + let type_enum = + ProtobufType::from_field(field, service, is_known_map(field, known_maps)); + let type_name = type_enum.to_tokens(); + //let wasm_type_name = type_enum.to_wasm_tokens(); + gen_fields.push(quote::quote! { pub #field_name: #type_name, }); - if let Some(name) = &field.type_name { - let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase()); - let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase()); - gen_into_fields.push( - quote::quote!{ - #field_name: #special_fn_into(self.#field_name), - } - ); + gen_into_fields.push(quote::quote! { + #field_name: self.#field_name.into(), + }); - gen_from_fields.push( - quote::quote!{ - #field_name: #special_fn_from(other.#field_name), - } - ); - } else { - gen_into_fields.push( - quote::quote!{ - #field_name: self.#field_name, - } - ); - - gen_from_fields.push( - quote::quote!{ - #field_name: other.#field_name, - } - ); - } + gen_from_fields.push(quote::quote! { + #field_name: <_>::from(other.#field_name), + }); } - let name = descriptor.name.clone().expect("Protobuf message needs a name"); - let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase()); - let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase()); + let wasm_attribute_maybe = + if (descriptor.field.len() == 1 || !is_response_msg) && !descriptor.field.is_empty() { + quote::quote! {} + } else { + quote::quote! { + #[wasm_bindgen] + } + }; - let wasm_attribute_maybe = if descriptor.field.len() == 1 || !is_response_msg { - quote::quote!{} - } else { - quote::quote!{ - #[wasm_bindgen] - } - }; - - quote::quote!{ + quote::quote! { #wasm_attribute_maybe pub struct #msg_name { #(#gen_fields)* } + impl KnownWasmCompatible for #msg_name {} + + impl IntoWasmable<#msg_name> for #msg_name { + fn into_wasm(self) -> Self { + self + } + } + + impl FromWasmable<#msg_name> for #msg_name { + fn from_wasm(x: Self) -> Self { + x + } + } + + type #msg_name_wasm = #msg_name; + impl std::convert::Into for #msg_name { #[inline] fn into(self) -> super::#super_msg_name { @@ -400,24 +397,11 @@ fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mu } } - #[inline] - #[allow(dead_code)] - fn #special_fn_from(other: super::#super_msg_name) -> #msg_name { - #msg_name::from(other) - } - - #[inline] - #[allow(dead_code)] - fn #special_fn_into(this: #msg_name) -> super::#super_msg_name { - this.into() - } - #(#gen_nested_types)* #(#gen_enums)* } } - } #[derive(Debug)] @@ -437,6 +421,11 @@ enum ProtobufType { Bool, String, Bytes, + Repeated(Box), + Map { + key: Box, + value: Box, + }, Custom(String), } @@ -464,62 +453,131 @@ impl ProtobufType { fn from_id(id: i32) -> Self { match id { - //"double" => quote::quote!{f64}, + 1 => Self::Double, //"float" => quote::quote!{f32}, //"int32" => quote::quote!{i32}, //"int64" => quote::quote!{i64}, //"uint32" => quote::quote!{u32}, - //"uint64" => quote::quote!{u64}, + 4 => Self::Uint64, + 5 => Self::Int32, //"sint32" => quote::quote!{i32}, //"sint64" => quote::quote!{i64}, //"fixed32" => quote::quote!{u32}, //"fixed64" => quote::quote!{u64}, //"sfixed32" => quote::quote!{i32}, //"sfixed64" => quote::quote!{i64}, - //"bool" => quote::quote!{bool}, + 8 => Self::Bool, 9 => Self::String, //"bytes" => quote::quote!{Vec}, t => Self::Custom(format!("UnknownType{}", t)), } } - fn from_field(field: &FieldDescriptorProto, service: &str) -> Self { - if let Some(type_name) = &field.type_name { + fn from_field(field: &FieldDescriptorProto, service: &str, is_map: bool) -> Self { + let inner = if let Some(type_name) = &field.type_name { Self::from_str(type_name, service) } else { let number = field.r#type.unwrap(); Self::from_id(number) - } + }; + if let Some(label) = field.label { + match label { + 3 if !is_map => Self::Repeated(Box::new(inner)), // is also the label for maps for some reason... + _ => inner, + } + } else { + inner + } } fn to_tokens(&self) -> proc_macro2::TokenStream { match self { - Self::Double => quote::quote!{f64}, - Self::Float => quote::quote!{f32}, - Self::Int32 => quote::quote!{i32}, - Self::Int64 => quote::quote!{i64}, - Self::Uint32 => quote::quote!{u32}, - Self::Uint64 => quote::quote!{u64}, - Self::Sint32 => quote::quote!{i32}, - Self::Sint64 => quote::quote!{i64}, - Self::Fixed32 => quote::quote!{u32}, - Self::Fixed64 => quote::quote!{u64}, - Self::Sfixed32 => quote::quote!{i32}, - Self::Sfixed64 => quote::quote!{i64}, - Self::Bool => quote::quote!{bool}, - Self::String => quote::quote!{String}, - Self::Bytes => quote::quote!{Vec}, + Self::Double => quote::quote! {f64}, + Self::Float => quote::quote! {f32}, + Self::Int32 => quote::quote! {i32}, + Self::Int64 => quote::quote! {i64}, + Self::Uint32 => quote::quote! {u32}, + Self::Uint64 => quote::quote! {u64}, + Self::Sint32 => quote::quote! {i32}, + Self::Sint64 => quote::quote! {i64}, + Self::Fixed32 => quote::quote! {u32}, + Self::Fixed64 => quote::quote! {u64}, + Self::Sfixed32 => quote::quote! {i32}, + Self::Sfixed64 => quote::quote! {i64}, + Self::Bool => quote::quote! {bool}, + Self::String => quote::quote! {String}, + Self::Bytes => quote::quote! {Vec}, + Self::Repeated(t) => { + let inner = t.to_tokens(); + quote::quote! {Vec::<#inner>} + } + Self::Map { key, value } => { + let key = key.to_tokens(); + let value = value.to_tokens(); + quote::quote! {std::collections::HashMap<#key, #value>} + } Self::Custom(t) => { let ident = quote::format_ident!("{}", t); - quote::quote!{#ident} - }, + quote::quote! {#ident} + } + } + } + + fn to_wasm_tokens(&self) -> proc_macro2::TokenStream { + match self { + Self::Double => quote::quote! {f64}, + Self::Float => quote::quote! {f32}, + Self::Int32 => quote::quote! {i32}, + Self::Int64 => quote::quote! {i64}, + Self::Uint32 => quote::quote! {u32}, + Self::Uint64 => quote::quote! {u64}, + Self::Sint32 => quote::quote! {i32}, + Self::Sint64 => quote::quote! {i64}, + Self::Fixed32 => quote::quote! {u32}, + Self::Fixed64 => quote::quote! {u64}, + Self::Sfixed32 => quote::quote! {i32}, + Self::Sfixed64 => quote::quote! {i64}, + Self::Bool => quote::quote! {bool}, + Self::String => quote::quote! {String}, + Self::Bytes => quote::quote! {Vec}, + Self::Repeated(_) => quote::quote! {js_sys::Array}, + Self::Map { .. } => quote::quote! {js_sys::Map}, + Self::Custom(t) => { + let ident = quote::format_ident!("{}Wasm", t); + quote::quote! {#ident} + } } } } -fn generate_wasm_enum_interop(descriptor: &EnumDescriptorProto, service: &str) -> proc_macro2::TokenStream { - let enum_name = quote::format_ident!("{}{}", service, descriptor.name.as_ref().expect("Protobuf enum needs a name")); - let super_enum_name = quote::format_ident!("{}", descriptor.name.as_ref().expect("Protobuf enum needs a name")); +fn generate_wasm_enum_interop( + descriptor: &EnumDescriptorProto, + service: &str, + seen_super_enums: &mut HashSet, +) -> proc_macro2::TokenStream { + let enum_name = quote::format_ident!( + "{}{}", + service, + descriptor + .name + .as_ref() + .expect("Protobuf enum needs a name") + ); + let enum_name_wasm = quote::format_ident!( + "{}{}Wasm", + service, + descriptor + .name + .as_ref() + .expect("Protobuf enum needs a name") + ); + let super_enum_name = quote::format_ident!( + "{}", + descriptor + .name + .as_ref() + .expect("Protobuf enum needs a name") + ); let mut gen_values = Vec::with_capacity(descriptor.value.len()); let mut gen_into_values = Vec::with_capacity(descriptor.value.len()); let mut gen_from_values = Vec::with_capacity(descriptor.value.len()); @@ -528,42 +586,46 @@ fn generate_wasm_enum_interop(descriptor: &EnumDescriptorProto, service: &str) - todo!("Deal with enum options when necessary"); } for value in &descriptor.value { - let val_name = quote::format_ident!("{}", value.name.as_ref().expect("Protobuf enum value needs a name")); + let val_name = quote::format_ident!( + "{}", + value + .name + .as_ref() + .expect("Protobuf enum value needs a name") + ); if let Some(_val_options) = &value.options { // TODO deal with options when necessary todo!("Deal with enum value options when necessary"); } else { if let Some(number) = &value.number { - gen_values.push( - quote::quote!{ - #val_name = #number, - } - ); + gen_values.push(quote::quote! { + #val_name = #number, + }); } else { - gen_values.push( - quote::quote!{ - #val_name, - } - ); + gen_values.push(quote::quote! { + #val_name, + }); } - gen_into_values.push( - quote::quote!{ - Self::#val_name => super::#super_enum_name::#val_name, - } - ); + gen_into_values.push(quote::quote! { + Self::#val_name => super::#super_enum_name::#val_name, + }); - gen_from_values.push( - quote::quote!{ - super::#super_enum_name::#val_name => Self::#val_name, - } - ); + gen_from_values.push(quote::quote! { + super::#super_enum_name::#val_name => Self::#val_name, + }); } } - let name = descriptor.name.clone().expect("Protobuf message needs a name"); - let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase()); - let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase()); - quote::quote!{ + let impl_wasm_compat = if seen_super_enums.contains(descriptor.name.as_ref().unwrap()) { + quote::quote! {} + } else { + seen_super_enums.insert(descriptor.name.clone().unwrap()); + quote::quote! { + //impl KnownWasmCompatible for super::#super_enum_name {} + } + }; + + quote::quote! { #[wasm_bindgen] #[repr(i32)] #[derive(Clone, Copy)] @@ -571,6 +633,8 @@ fn generate_wasm_enum_interop(descriptor: &EnumDescriptorProto, service: &str) - #(#gen_values)* } + type #enum_name_wasm = #enum_name; + impl std::convert::Into for #enum_name { fn into(self) -> super::#super_enum_name { match self { @@ -587,66 +651,137 @@ fn generate_wasm_enum_interop(descriptor: &EnumDescriptorProto, service: &str) - } } - #[inline] - #[allow(dead_code)] - fn #special_fn_from(other: i32) -> #enum_name { - #enum_name::from(super::#super_enum_name::from_i32(other).unwrap()) + #impl_wasm_compat + + impl FromWasmable for #enum_name { + fn from_wasm(js: i32) -> Self { + #enum_name::from(super::#super_enum_name::from_i32(js).unwrap()) + } } - #[inline] - #[allow(dead_code)] - fn #special_fn_into(this: #enum_name) -> i32 { - this as i32 + impl IntoWasmable for #enum_name { + fn into_wasm(self) -> i32 { + self as i32 + } + } + + impl From for #enum_name { + fn from(other: i32) -> Self { + #enum_name::from(super::#super_enum_name::from_i32(other).unwrap()) + } + } + + impl Into for #enum_name { + fn into(self) -> i32 { + self as i32 + } } } } -fn generate_service_io_types(service: &Service, fds: &FileDescriptorSet) -> proc_macro2::TokenStream { +fn generate_service_io_types( + service: &Service, + fds: &FileDescriptorSet, +) -> proc_macro2::TokenStream { let mut gen_types = Vec::with_capacity(service.methods.len() * 2); let mut gen_enums = Vec::new(); let mut handled_enums = HashSet::new(); let mut handled_types = HashSet::new(); + let mut known_maps = HashSet::new(); + let mut seen_super_enums = HashSet::new(); for method in &service.methods { if let Some(input_message) = find_message_type(&method.input_type, &service.package, fds) { - let msg_name = input_message.name.clone().expect("Protobuf message name required"); + let msg_name = input_message + .name + .clone() + .expect("Protobuf message name required"); if !handled_types.contains(&msg_name) { handled_types.insert(msg_name); - gen_types.push(generate_wasm_struct_interop(input_message, &mut handled_enums, &mut handled_types, false, &service.name)); + gen_types.push(generate_wasm_struct_interop( + input_message, + &mut handled_enums, + &mut handled_types, + &mut known_maps, + &mut seen_super_enums, + false, + &service.name, + )); } } else if let Some(input_enum) = find_enum_type(&method.input_type, &service.package, fds) { - let enum_name = input_enum.name.clone().expect("Protobuf enum name required"); + let enum_name = input_enum + .name + .clone() + .expect("Protobuf enum name required"); if !handled_enums.contains(&enum_name) { handled_enums.insert(enum_name); - gen_types.push(generate_wasm_enum_interop(input_enum, &service.name)); + gen_enums.push(generate_wasm_enum_interop( + input_enum, + &service.name, + &mut seen_super_enums, + )); } } else { - panic!("Cannot find proto type {}/{}", service.package, method.input_type); + panic!( + "Cannot find input proto type/message {}/{} for method {}", + service.package, method.input_type, method.name + ); } - if let Some(output_message) = find_message_type(&method.output_type, &service.package, fds) { - let msg_name = output_message.name.clone().expect("Protobuf message name required"); + if let Some(output_message) = find_message_type(&method.output_type, &service.package, fds) + { + let msg_name = output_message + .name + .clone() + .expect("Protobuf message name required"); if !handled_types.contains(&msg_name) { handled_types.insert(msg_name); - gen_types.push(generate_wasm_struct_interop(output_message, &mut handled_enums, &mut handled_types, true, &service.name)); + gen_types.push(generate_wasm_struct_interop( + output_message, + &mut handled_enums, + &mut handled_types, + &mut known_maps, + &mut seen_super_enums, + true, + &service.name, + )); } - } else if let Some(output_enum) = find_enum_type(&method.output_type, &service.package, fds) { - let enum_name = output_enum.name.clone().expect("Protobuf enum name required"); + } else if let Some(output_enum) = find_enum_type(&method.output_type, &service.package, fds) + { + let enum_name = output_enum + .name + .clone() + .expect("Protobuf enum name required"); if !handled_enums.contains(&enum_name) { handled_enums.insert(enum_name); - gen_types.push(generate_wasm_enum_interop(output_enum, &service.name)); + gen_enums.push(generate_wasm_enum_interop( + output_enum, + &service.name, + &mut seen_super_enums, + )); } } else { - panic!("Cannot find proto type {}/{}", service.package, method.input_type); + panic!( + "Cannot find output proto type/message {}/{} for method {}", + service.package, method.output_type, method.name + ); } } // always generate all enums, since they aren't encountered (ever, afaik) when generating message structs for file in &fds.file { - for enum_type in &file.enum_type { - let enum_name = enum_type.name.clone().expect("Protobuf enum name required"); - if !handled_enums.contains(&enum_name) { - handled_enums.insert(enum_name); - gen_enums.push(generate_wasm_enum_interop(enum_type, &service.name)); + if let Some(pkg) = &file.package { + if pkg == &service.package { + for enum_type in &file.enum_type { + let enum_name = enum_type.name.clone().expect("Protobuf enum name required"); + if !handled_enums.contains(&enum_name) { + handled_enums.insert(enum_name); + gen_enums.push(generate_wasm_enum_interop( + enum_type, + &service.name, + &mut seen_super_enums, + )); + } + } } } } @@ -659,9 +794,9 @@ fn generate_service_io_types(service: &Service, fds: &FileDescriptorSet) -> proc impl IServiceGenerator for WasmServiceGenerator { fn generate(&mut self, service: Service) -> proc_macro2::TokenStream { - let lock = self.shared.lock() - .expect("Cannot lock shared state"); - let fds = lock.fds + let lock = self.shared.lock().expect("Cannot lock shared state"); + let fds = lock + .fds .as_ref() .expect("FileDescriptorSet required for WASM service generator"); let service_struct_name = quote::format_ident!("{}Client", service.name); @@ -669,11 +804,17 @@ impl IServiceGenerator for WasmServiceGenerator { let service_methods = generate_service_methods(&service, fds); let service_types = generate_service_io_types(&service, fds); let mod_name = quote::format_ident!("js_{}", service.name.to_lowercase()); - quote::quote!{ + quote::quote! { mod #mod_name { - use wasm_bindgen::prelude::*; + #![allow(dead_code, unused_imports)] + use usdpl_front::_helpers::wasm_bindgen::prelude::*; + use usdpl_front::_helpers::wasm_bindgen; + use usdpl_front::_helpers::wasm_bindgen_futures; + use usdpl_front::_helpers::js_sys; - use crate::client_handler::WebSocketHandler; + use usdpl_front::wasm::*; + + use usdpl_front::WebSocketHandler; #service_types diff --git a/usdpl-build/src/front/shared_state.rs b/usdpl-build/src/front/shared_state.rs index 2175d1d..fb098f3 100644 --- a/usdpl-build/src/front/shared_state.rs +++ b/usdpl-build/src/front/shared_state.rs @@ -7,9 +7,7 @@ pub struct SharedState(Arc>); impl SharedState { pub fn new() -> Self { - Self(Arc::new(Mutex::new(SharedProtoData { - fds: None, - }))) + Self(Arc::new(Mutex::new(SharedProtoData { fds: None }))) } } diff --git a/usdpl-build/src/lib.rs b/usdpl-build/src/lib.rs index d543e54..3fd10ac 100644 --- a/usdpl-build/src/lib.rs +++ b/usdpl-build/src/lib.rs @@ -2,4 +2,6 @@ pub mod back; pub mod front; mod proto_files; -pub use proto_files::{dump_protos, dump_protos_out, proto_out_paths, all_proto_filenames, proto_builtins_out_path}; +pub use proto_files::{ + all_proto_filenames, dump_protos, dump_protos_out, proto_builtins_out_path, proto_out_paths, +}; diff --git a/usdpl-build/src/proto_files.rs b/usdpl-build/src/proto_files.rs index 58e4bed..d15570e 100644 --- a/usdpl-build/src/proto_files.rs +++ b/usdpl-build/src/proto_files.rs @@ -17,26 +17,25 @@ const TRANSLATIONS_PROTO: IncludedFileStr<'static> = IncludedFileStr { contents: include_str!("../protos/translations.proto"), }; -const ALL_PROTOS: [IncludedFileStr<'static>; 2] = [ - DEBUG_PROTO, - TRANSLATIONS_PROTO, -]; +const ALL_PROTOS: [IncludedFileStr<'static>; 2] = [DEBUG_PROTO, TRANSLATIONS_PROTO]; pub fn proto_builtins_out_path() -> PathBuf { - PathBuf::from(std::env::var("OUT_DIR").expect("Not in a build.rs context (missing $OUT_DIR)")).join("protos") + PathBuf::from(std::env::var("OUT_DIR").expect("Not in a build.rs context (missing $OUT_DIR)")) + .join("protos") } -pub fn proto_out_paths() -> impl Iterator { +pub fn proto_out_paths(additionals: impl Iterator) -> impl Iterator { std::iter::once(proto_builtins_out_path()) .map(|x| x.to_str().unwrap().to_owned()) - .chain(custom_protos_dirs().into_iter()) + .chain(custom_protos_dirs(additionals).into_iter()) } -fn custom_protos_dirs() -> Vec { +fn custom_protos_dirs(additionals: impl Iterator) -> Vec { let dirs = std::env::var(ADDITIONAL_PROTOBUFS_ENV_VAR).unwrap_or_else(|_| "".to_owned()); dirs.split(':') .filter(|x| std::fs::read_dir(x).is_ok()) .map(|x| x.to_owned()) + .chain(additionals) .collect() } @@ -48,14 +47,27 @@ fn custom_protos_filenames() -> Vec { .flat_map(|x| x.unwrap()) .filter(|x| x.is_ok()) .map(|x| x.unwrap().path()) - .filter(|x| if let Some(ext) = x.extension() { ext.to_ascii_lowercase() == "proto" && x.is_file() } else { false }) + .filter(|x| { + if let Some(ext) = x.extension() { + ext.to_ascii_lowercase() == "proto" && x.is_file() + } else { + false + } + }) .filter_map(|x| x.to_str().map(|x| x.to_owned())) .collect() } -pub fn all_proto_filenames(p: impl AsRef + 'static) -> impl Iterator { +pub fn all_proto_filenames( + p: impl AsRef + 'static, + additionals: impl Iterator, +) -> impl Iterator { //let p = p.as_ref(); - ALL_PROTOS.iter().map(move |x| p.as_ref().join(x.filename).to_str().unwrap().to_owned()).chain(custom_protos_filenames()) + ALL_PROTOS + .iter() + .map(move |x| p.as_ref().join(x.filename).to_str().unwrap().to_owned()) + .chain(custom_protos_filenames()) + .chain(additionals) } pub fn dump_protos(p: impl AsRef) -> std::io::Result<()> { diff --git a/usdpl-core/src/lib.rs b/usdpl-core/src/lib.rs index 7090ffa..f5453bc 100644 --- a/usdpl-core/src/lib.rs +++ b/usdpl-core/src/lib.rs @@ -4,12 +4,10 @@ mod remote_call; -#[cfg(not(any(feature = "decky", feature = "crankshaft")))] +#[cfg(not(any(feature = "decky")))] mod api_any; mod api_common; -#[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))] -mod api_crankshaft; -#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))] +#[cfg(all(feature = "decky", not(any(feature = "any"))))] mod api_decky; pub mod serdes; @@ -20,11 +18,9 @@ pub use remote_call::{RemoteCall, RemoteCallResponse}; /// USDPL core API. /// This contains functionality used in both the back-end and front-end. pub mod api { - #[cfg(not(any(feature = "decky", feature = "crankshaft")))] + #[cfg(not(any(feature = "decky")))] pub use super::api_any::*; pub use super::api_common::*; - #[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))] - pub use super::api_crankshaft::*; - #[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))] + #[cfg(all(feature = "decky", not(any(feature = "any"))))] pub use super::api_decky::*; } diff --git a/usdpl-core/src/remote_call.rs b/usdpl-core/src/remote_call.rs index e189304..973f0db 100644 --- a/usdpl-core/src/remote_call.rs +++ b/usdpl-core/src/remote_call.rs @@ -73,7 +73,7 @@ mod tests { #[test] fn remote_call_idempotence_test() { - let call = RemoteCall{ + let call = RemoteCall { id: 42, function: "something very long just in case this causes unexpected issues".into(), parameters: vec!["param1".into(), 42f64.into()], @@ -88,7 +88,10 @@ mod tests { assert_eq!(len, loaded_len, "Expected load and dump lengths to match"); assert_eq!(loaded_call.id, call.id, "RemoteCall.id does not match"); - assert_eq!(loaded_call.function, call.function, "RemoteCall.function does not match"); + assert_eq!( + loaded_call.function, call.function, + "RemoteCall.function does not match" + ); if let Primitive::String(loaded) = &loaded_call.parameters[0] { if let Primitive::String(original) = &call.parameters[0] { assert_eq!(loaded, original, "RemoteCall.parameters[0] does not match"); diff --git a/usdpl-core/src/serdes/dump_impl.rs b/usdpl-core/src/serdes/dump_impl.rs index a74f08c..61551fe 100644 --- a/usdpl-core/src/serdes/dump_impl.rs +++ b/usdpl-core/src/serdes/dump_impl.rs @@ -26,43 +26,34 @@ impl Dumpable for Vec { impl Dumpable for (T0, T1) { fn dump(&self, buffer: &mut dyn Write) -> Result { - Ok( - self.0.dump(buffer)? - + self.1.dump(buffer)? - ) + Ok(self.0.dump(buffer)? + self.1.dump(buffer)?) } } impl Dumpable for (T0, T1, T2) { fn dump(&self, buffer: &mut dyn Write) -> Result { - Ok( - self.0.dump(buffer)? - + self.1.dump(buffer)? - + self.2.dump(buffer)? - ) + Ok(self.0.dump(buffer)? + self.1.dump(buffer)? + self.2.dump(buffer)?) } } impl Dumpable for (T0, T1, T2, T3) { fn dump(&self, buffer: &mut dyn Write) -> Result { - Ok( - self.0.dump(buffer)? + Ok(self.0.dump(buffer)? + self.1.dump(buffer)? + self.2.dump(buffer)? - + self.3.dump(buffer)? - ) + + self.3.dump(buffer)?) } } -impl Dumpable for (T0, T1, T2, T3, T4) { +impl Dumpable + for (T0, T1, T2, T3, T4) +{ fn dump(&self, buffer: &mut dyn Write) -> Result { - Ok( - self.0.dump(buffer)? + Ok(self.0.dump(buffer)? + self.1.dump(buffer)? + self.2.dump(buffer)? + self.3.dump(buffer)? - + self.4.dump(buffer)? - ) + + self.4.dump(buffer)?) } } diff --git a/usdpl-core/src/serdes/load_impl.rs b/usdpl-core/src/serdes/load_impl.rs index 2e37097..f2aa9bf 100644 --- a/usdpl-core/src/serdes/load_impl.rs +++ b/usdpl-core/src/serdes/load_impl.rs @@ -42,10 +42,7 @@ impl Loadable for (T0, T1) { fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { let (t0, len0) = T0::load(buffer)?; let (t1, len1) = T1::load(buffer)?; - Ok(( - (t0, t1), - len0 + len1 - )) + Ok(((t0, t1), len0 + len1)) } } @@ -54,10 +51,7 @@ impl Loadable for (T0, T1, T2) { let (t0, len0) = T0::load(buffer)?; let (t1, len1) = T1::load(buffer)?; let (t2, len2) = T2::load(buffer)?; - Ok(( - (t0, t1, t2), - len0 + len1 + len2 - )) + Ok(((t0, t1, t2), len0 + len1 + len2)) } } @@ -67,24 +61,20 @@ impl Loadable for (T0, T let (t1, len1) = T1::load(buffer)?; let (t2, len2) = T2::load(buffer)?; let (t3, len3) = T3::load(buffer)?; - Ok(( - (t0, t1, t2, t3), - len0 + len1 + len2 + len3 - )) + Ok(((t0, t1, t2, t3), len0 + len1 + len2 + len3)) } } -impl Loadable for (T0, T1, T2, T3, T4) { +impl Loadable + for (T0, T1, T2, T3, T4) +{ fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { let (t0, len0) = T0::load(buffer)?; let (t1, len1) = T1::load(buffer)?; let (t2, len2) = T2::load(buffer)?; let (t3, len3) = T3::load(buffer)?; let (t4, len4) = T4::load(buffer)?; - Ok(( - (t0, t1, t2, t3, t4), - len0 + len1 + len2 + len3 + len4 - )) + Ok(((t0, t1, t2, t3, t4), len0 + len1 + len2 + len3 + len4)) } } diff --git a/usdpl-core/src/serdes/primitive.rs b/usdpl-core/src/serdes/primitive.rs index d652ea1..9793bff 100644 --- a/usdpl-core/src/serdes/primitive.rs +++ b/usdpl-core/src/serdes/primitive.rs @@ -1,5 +1,5 @@ -use std::io::{Read, Write}; use super::{DumpError, Dumpable, LoadError, Loadable}; +use std::io::{Read, Write}; /// Primitive types supported for communication between the USDPL back- and front-end. /// These are used for sending over the TCP connection. @@ -47,7 +47,8 @@ impl Primitive { impl Loadable for Primitive { fn load(buf: &mut dyn Read) -> Result<(Self, usize), LoadError> { let mut discriminant_buf = [u8::MAX; 1]; - buf.read_exact(&mut discriminant_buf).map_err(LoadError::Io)?; + buf.read_exact(&mut discriminant_buf) + .map_err(LoadError::Io)?; let mut result: (Self, usize) = match discriminant_buf[0] { //0 => (None, 0), 1 => (Self::Empty, 0), @@ -105,7 +106,7 @@ macro_rules! into_impl { Primitive::$variant(self) } } - } + }; } into_impl! {String, String} diff --git a/usdpl-core/src/serdes/traits.rs b/usdpl-core/src/serdes/traits.rs index 07bc2fd..b11b5db 100644 --- a/usdpl-core/src/serdes/traits.rs +++ b/usdpl-core/src/serdes/traits.rs @@ -1,5 +1,5 @@ -use std::io::{Read, Write, Cursor}; use base64::{decode_config_buf, encode_config_buf, Config}; +use std::io::{Cursor, Read, Write}; const B64_CONF: Config = Config::new(base64::CharacterSet::Standard, true); @@ -49,8 +49,7 @@ pub trait Loadable: Sized { /// Load data from a base64-encoded buffer fn load_base64(buffer: &[u8]) -> Result<(Self, usize), LoadError> { let mut buffer2 = Vec::with_capacity(crate::socket::PACKET_BUFFER_SIZE); - decode_config_buf(buffer, B64_CONF, &mut buffer2) - .map_err(|_| LoadError::InvalidData)?; + decode_config_buf(buffer, B64_CONF, &mut buffer2).map_err(|_| LoadError::InvalidData)?; let mut cursor = Cursor::new(buffer2); Self::load(&mut cursor) } @@ -66,7 +65,9 @@ pub trait Loadable: Sized { base64::decode_config_buf(buffer, B64_CONF, &mut decoded_buf) .map_err(|_| LoadError::InvalidData)?; //println!("Decoded buf: {:?}", decoded_buf); - cipher.decrypt_in_place(nonce, ASSOCIATED_DATA, &mut decoded_buf).map_err(|_| LoadError::DecryptionError)?; + cipher + .decrypt_in_place(nonce, ASSOCIATED_DATA, &mut decoded_buf) + .map_err(|_| LoadError::DecryptionError)?; //println!("Decrypted buf: {:?}", decoded_buf); let mut cursor = Cursor::new(decoded_buf); Self::load(&mut cursor) @@ -121,7 +122,12 @@ pub trait Dumpable { /// Dump data as an encrypted base64-encoded buffer #[cfg(feature = "encrypt")] - fn dump_encrypted(&self, buffer: &mut Vec, key: &[u8], nonce: &[u8]) -> Result { + fn dump_encrypted( + &self, + buffer: &mut Vec, + key: &[u8], + nonce: &[u8], + ) -> Result { let mut buffer2 = Vec::with_capacity(crate::socket::PACKET_BUFFER_SIZE); let size = self.dump(&mut buffer2)?; buffer2.truncate(size); @@ -129,7 +135,9 @@ pub trait Dumpable { let key = aes_gcm_siv::Key::from_slice(key); let cipher = aes_gcm_siv::Aes256GcmSiv::new(key); let nonce = aes_gcm_siv::Nonce::from_slice(nonce); - cipher.encrypt_in_place(nonce, ASSOCIATED_DATA, &mut buffer2).map_err(|_| DumpError::EncryptionError)?; + cipher + .encrypt_in_place(nonce, ASSOCIATED_DATA, &mut buffer2) + .map_err(|_| DumpError::EncryptionError)?; //println!("Encrypted slice: {:?}", &buffer2); let mut base64_buf = String::with_capacity(crate::socket::PACKET_BUFFER_SIZE); encode_config_buf(buffer2.as_slice(), B64_CONF, &mut base64_buf); diff --git a/usdpl-core/src/socket.rs b/usdpl-core/src/socket.rs index 0f72a01..6b8940e 100644 --- a/usdpl-core/src/socket.rs +++ b/usdpl-core/src/socket.rs @@ -1,6 +1,6 @@ //! Web messaging -use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use std::io::{Read, Write}; +use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use crate::serdes::{DumpError, Dumpable, LoadError, Loadable}; use crate::{RemoteCall, RemoteCallResponse}; @@ -66,7 +66,8 @@ impl Packet { impl Loadable for Packet { fn load(buf: &mut dyn Read) -> Result<(Self, usize), LoadError> { let mut discriminant_buf = [u8::MAX; 1]; - buf.read_exact(&mut discriminant_buf).map_err(LoadError::Io)?; + buf.read_exact(&mut discriminant_buf) + .map_err(LoadError::Io)?; let mut result: (Self, usize) = match discriminant_buf[0] { //0 => (None, 0), 1 => { @@ -88,15 +89,15 @@ impl Loadable for Packet { 8 => { let (obj, len) = <_>::load(buf)?; (Self::Many(obj), len) - }, + } 9 => { let (obj, len) = <_>::load(buf)?; (Self::Translations(obj), len) - }, + } 10 => { let (obj, len) = <_>::load(buf)?; (Self::Language(obj), len) - }, + } _ => return Err(LoadError::InvalidData), }; result.1 += 1; @@ -130,24 +131,39 @@ mod tests { #[cfg(feature = "encrypt")] #[test] fn encryption_integration_test() { - let key = hex_literal::hex!("59C4E408F27250B3147E7724511824F1D28ED7BEF43CF7103ACE747F77A2B265"); + let key = + hex_literal::hex!("59C4E408F27250B3147E7724511824F1D28ED7BEF43CF7103ACE747F77A2B265"); let nonce = [0u8; NONCE_SIZE]; - let packet = Packet::Call(RemoteCall{ + let packet = Packet::Call(RemoteCall { id: 42, function: "test".into(), parameters: Vec::new(), }); let mut buffer = Vec::with_capacity(PACKET_BUFFER_SIZE); let len = packet.dump_encrypted(&mut buffer, &key, &nonce).unwrap(); - println!("buffer: {}", String::from_utf8(buffer.as_slice()[..len].to_vec()).unwrap()); + println!( + "buffer: {}", + String::from_utf8(buffer.as_slice()[..len].to_vec()).unwrap() + ); - let (packet_out, _len) = Packet::load_encrypted(&buffer.as_slice()[..len], &key, &nonce).unwrap(); + let (packet_out, _len) = + Packet::load_encrypted(&buffer.as_slice()[..len], &key, &nonce).unwrap(); if let Packet::Call(call_out) = packet_out { if let Packet::Call(call_in) = packet { - assert_eq!(call_in.id, call_out.id, "Input and output packets do not match"); - assert_eq!(call_in.function, call_out.function, "Input and output packets do not match"); - assert_eq!(call_in.parameters.len(), call_out.parameters.len(), "Input and output packets do not match"); + assert_eq!( + call_in.id, call_out.id, + "Input and output packets do not match" + ); + assert_eq!( + call_in.function, call_out.function, + "Input and output packets do not match" + ); + assert_eq!( + call_in.parameters.len(), + call_out.parameters.len(), + "Input and output packets do not match" + ); } else { panic!("Packet in not a Call"); } diff --git a/usdpl-front/Cargo.toml b/usdpl-front/Cargo.toml index afffe0c..a28ea0e 100644 --- a/usdpl-front/Cargo.toml +++ b/usdpl-front/Cargo.toml @@ -50,6 +50,3 @@ prost = "0.11" [dev-dependencies] wasm-bindgen-test = { version = "0.3.13" } - -[build-dependencies] -usdpl-build = { version = "0.11", path = "../usdpl-build" } diff --git a/usdpl-front/build.rs b/usdpl-front/build.rs deleted file mode 100644 index 16e4f14..0000000 --- a/usdpl-front/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - usdpl_build::front::build() -} diff --git a/usdpl-front/src/client_handler.rs b/usdpl-front/src/client_handler.rs index b592370..7d8fe94 100644 --- a/usdpl-front/src/client_handler.rs +++ b/usdpl-front/src/client_handler.rs @@ -1,20 +1,23 @@ use std::sync::atomic::{AtomicU64, Ordering}; -use nrpc::{ClientHandler, ServiceError, _helpers::bytes, _helpers::async_trait}; -use gloo_net::websocket::{Message, futures::WebSocket}; -use wasm_bindgen_futures::spawn_local; use futures::{SinkExt, StreamExt}; +use gloo_net::websocket::{futures::WebSocket, Message}; +use nrpc::{ClientHandler, ServiceError, _helpers::async_trait, _helpers::bytes}; +use wasm_bindgen_futures::spawn_local; static LAST_ID: AtomicU64 = AtomicU64::new(0); +/// Websocket client. +/// In most cases, this shouldn't be used directly, but generated code will use this. pub struct WebSocketHandler { - // TODO port: u16, } async fn send_recv_ws(url: String, input: bytes::Bytes) -> Result, String> { let mut ws = WebSocket::open(&url).map_err(|e| e.to_string())?; - ws.send(Message::Bytes(input.into())).await.map_err(|e| e.to_string())?; + ws.send(Message::Bytes(input.into())) + .await + .map_err(|e| e.to_string())?; read_next_incoming(ws).await } @@ -42,7 +45,7 @@ impl std::fmt::Display for ErrorStr { impl std::error::Error for ErrorStr {} impl WebSocketHandler { - #[allow(dead_code)] + /// Instantiate the web socket client for connecting on the specified port pub fn new(port: u16) -> Self { Self { port } } @@ -50,33 +53,29 @@ impl WebSocketHandler { #[async_trait::async_trait] impl ClientHandler for WebSocketHandler { - async fn call(&mut self, - package: &str, - service: &str, - method: &str, - input: bytes::Bytes, - output: &mut bytes::BytesMut) -> Result<(), ServiceError> { + async fn call( + &mut self, + package: &str, + service: &str, + method: &str, + input: bytes::Bytes, + output: &mut bytes::BytesMut, + ) -> Result<(), ServiceError> { let id = LAST_ID.fetch_add(1, Ordering::SeqCst); let url = format!( "ws://usdpl-ws-{}.localhost:{}/{}.{}/{}", - id, - self.port, - package, - service, - method, + id, self.port, package, service, method, ); let (tx, rx) = async_channel::bounded(1); spawn_local(async move { - tx.send(send_recv_ws( - url, - input - ).await).await.unwrap_or(()); + tx.send(send_recv_ws(url, input).await).await.unwrap_or(()); }); output.extend_from_slice( - &rx.recv().await + &rx.recv() + .await .map_err(|e| ServiceError::Method(Box::new(e)))? - .map_err(|e| ServiceError::Method(Box::new(ErrorStr(e))))? + .map_err(|e| ServiceError::Method(Box::new(ErrorStr(e))))?, ); Ok(()) } diff --git a/usdpl-front/src/connection.rs b/usdpl-front/src/connection.rs index a41979a..442e312 100644 --- a/usdpl-front/src/connection.rs +++ b/usdpl-front/src/connection.rs @@ -14,26 +14,33 @@ use usdpl_core::serdes::{Dumpable, Loadable, Primitive}; use usdpl_core::socket; #[cfg(feature = "encrypt")] -const NONCE: [u8; socket::NONCE_SIZE]= [0u8; socket::NONCE_SIZE]; +const NONCE: [u8; socket::NONCE_SIZE] = [0u8; socket::NONCE_SIZE]; pub async fn send_recv_packet( id: u64, packet: socket::Packet, port: u16, - #[cfg(feature = "encrypt")] - key: Vec, + #[cfg(feature = "encrypt")] key: Vec, ) -> Result { - let mut opts = RequestInit::new(); opts.method("POST"); opts.mode(RequestMode::Cors); - let url = format!("http://usdpl{}.{}:{}/usdpl/call", id, socket::HOST_STR, port); + let url = format!( + "http://usdpl{}.{}:{}/usdpl/call", + id, + socket::HOST_STR, + port + ); #[allow(unused_variables)] - let (buffer, len) = dump_to_buffer(packet, #[cfg(feature = "encrypt")] key.as_slice())?; + let (buffer, len) = dump_to_buffer( + packet, + #[cfg(feature = "encrypt")] + key.as_slice(), + )?; let string: String = String::from_utf8_lossy(buffer.as_slice()).into(); - #[cfg(feature="debug")] + #[cfg(feature = "debug")] crate::imports::console_log(&format!("Dumped base64 `{}` len:{}", string, len)); opts.body(Some(&string.into())); @@ -50,31 +57,46 @@ pub async fn send_recv_packet( let string: JsString = text.dyn_into()?; let rust_str = string.as_string().unwrap(); - #[cfg(feature="debug")] - crate::imports::console_log(&format!("Received base64 `{}` len:{}", rust_str, rust_str.len())); + #[cfg(feature = "debug")] + crate::imports::console_log(&format!( + "Received base64 `{}` len:{}", + rust_str, + rust_str.len() + )); #[cfg(not(feature = "encrypt"))] - {Ok(socket::Packet::load_base64(rust_str.as_bytes()) - .map_err(super::convert::str_to_js)? - .0)} + { + Ok(socket::Packet::load_base64(rust_str.as_bytes()) + .map_err(super::convert::str_to_js)? + .0) + } #[cfg(feature = "encrypt")] - {Ok(socket::Packet::load_encrypted(rust_str.as_bytes(), key.as_slice(), &NONCE) - .map_err(super::convert::str_to_js)? - .0)} + { + Ok( + socket::Packet::load_encrypted(rust_str.as_bytes(), key.as_slice(), &NONCE) + .map_err(super::convert::str_to_js)? + .0, + ) + } } pub async fn send_call( id: u64, packet: socket::Packet, port: u16, - #[cfg(feature = "encrypt")] - key: Vec, + #[cfg(feature = "encrypt")] key: Vec, ) -> Result, JsValue> { - let packet = send_recv_packet(id, packet, port, #[cfg(feature = "encrypt")] key).await?; + let packet = send_recv_packet( + id, + packet, + port, + #[cfg(feature = "encrypt")] + key, + ) + .await?; - match packet - { + match packet { socket::Packet::CallResponse(resp) => Ok(resp.response), _ => { //imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", resp.url())); diff --git a/usdpl-front/src/lib.rs b/usdpl-front/src/lib.rs index dd173f2..96942aa 100644 --- a/usdpl-front/src/lib.rs +++ b/usdpl-front/src/lib.rs @@ -6,13 +6,22 @@ #![warn(missing_docs)] mod client_handler; +pub use client_handler::WebSocketHandler; mod connection; mod convert; mod imports; +pub mod wasm; -#[allow(missing_docs)] // existence is pain otherwise +/*#[allow(missing_docs)] // existence is pain otherwise pub mod _nrpc_js_interop { - include!(concat!(env!("OUT_DIR"), "/usdpl.rs")); + include!(concat!(env!("OUT_DIR"), "/mod.rs")); +}*/ + +#[allow(missing_docs)] +pub mod _helpers { + pub use js_sys; + pub use wasm_bindgen; + pub use wasm_bindgen_futures; } use std::sync::atomic::{AtomicU64, Ordering}; @@ -99,7 +108,11 @@ pub fn version_usdpl() -> String { #[wasm_bindgen] pub fn set_value(key: String, value: JsValue) -> JsValue { unsafe { - CACHE.as_mut().unwrap().insert(key, value).unwrap_or(JsValue::NULL) + CACHE + .as_mut() + .unwrap() + .insert(key, value) + .unwrap_or(JsValue::NULL) } } @@ -107,7 +120,12 @@ pub fn set_value(key: String, value: JsValue) -> JsValue { #[wasm_bindgen] pub fn get_value(key: String) -> JsValue { unsafe { - CACHE.as_ref().unwrap().get(&key).map(|x| x.to_owned()).unwrap_or(JsValue::UNDEFINED) + CACHE + .as_ref() + .unwrap() + .get(&key) + .map(|x| x.to_owned()) + .unwrap_or(JsValue::UNDEFINED) } } @@ -138,7 +156,7 @@ pub async fn call_backend(name: String, parameters: Vec) -> JsValue { }), port, #[cfg(feature = "encrypt")] - get_key() + get_key(), ) .await; let results = match results { @@ -168,8 +186,10 @@ pub async fn init_tr(locale: String) { Packet::Language(locale.clone()), get_port(), #[cfg(feature = "encrypt")] - get_key() - ).await { + get_key(), + ) + .await + { Ok(Packet::Translations(translations)) => { #[cfg(feature = "debug")] imports::console_log(&format!("USDPL: Got translations for {}", locale)); @@ -179,12 +199,12 @@ pub async fn init_tr(locale: String) { tr_map.insert(key, val); } unsafe { TRANSLATIONS = Some(tr_map) } - }, + } Ok(_) => { #[cfg(feature = "debug")] imports::console_error(&format!("USDPL: Got wrong packet response for init_tr")); unsafe { TRANSLATIONS = None } - }, + } #[allow(unused_variables)] Err(e) => { #[cfg(feature = "debug")] diff --git a/usdpl-front/src/wasm/arrays.rs b/usdpl-front/src/wasm/arrays.rs new file mode 100644 index 0000000..45dd935 --- /dev/null +++ b/usdpl-front/src/wasm/arrays.rs @@ -0,0 +1,94 @@ +use js_sys::Array; + +use super::{FromWasmable, IntoWasmable}; + +macro_rules! numbers_array { + ($num_ty: ident) => { + impl FromWasmable for Vec<$num_ty> { + fn from_wasm(js: Array) -> Self { + let mut result = Vec::with_capacity(js.length() as usize); + js.for_each(&mut |val, _index, _arr| { + // according to MDN, this is guaranteed to be in order so index can be ignored + if let Some(val) = val.as_f64() { + result.push(val as $num_ty); + } + }); + result + } + } + + impl IntoWasmable for Vec<$num_ty> { + fn into_wasm(self) -> Array { + let result = Array::new(); + for val in self { + result.push(&val.into()); + } + result + } + } + }; +} + +numbers_array! { f64 } +numbers_array! { f32 } + +numbers_array! { isize } +numbers_array! { usize } + +numbers_array! { i8 } +numbers_array! { i16 } +numbers_array! { i32 } +numbers_array! { i64 } +numbers_array! { i128 } + +numbers_array! { u8 } +numbers_array! { u16 } +numbers_array! { u32 } +numbers_array! { u64 } +numbers_array! { u128 } + +impl FromWasmable for Vec { + fn from_wasm(js: Array) -> Self { + let mut result = Vec::with_capacity(js.length() as usize); + js.for_each(&mut |val, _index, _arr| { + // according to MDN, this is guaranteed to be in order so index can be ignored + if let Some(val) = val.as_string() { + result.push(val); + } + }); + result + } +} + +impl IntoWasmable for Vec { + fn into_wasm(self) -> Array { + let result = Array::new(); + for val in self { + result.push(&val.into()); + } + result + } +} + +impl FromWasmable for Vec { + fn from_wasm(js: Array) -> Self { + let mut result = Vec::with_capacity(js.length() as usize); + js.for_each(&mut |val, _index, _arr| { + // according to MDN, this is guaranteed to be in order so index can be ignored + if let Some(val) = val.as_bool() { + result.push(val); + } + }); + result + } +} + +impl IntoWasmable for Vec { + fn into_wasm(self) -> Array { + let result = Array::new(); + for val in self { + result.push(&val.into()); + } + result + } +} diff --git a/usdpl-front/src/wasm/maps.rs b/usdpl-front/src/wasm/maps.rs new file mode 100644 index 0000000..d7dcbcd --- /dev/null +++ b/usdpl-front/src/wasm/maps.rs @@ -0,0 +1,99 @@ +use std::collections::HashMap; + +use js_sys::Map; + +use super::{FromWasmable, IntoWasmable}; + +macro_rules! numbers_map { + ($num_ty: ident) => { + impl FromWasmable for HashMap { + fn from_wasm(js: Map) -> Self { + let mut result = HashMap::with_capacity(js.size() as usize); + js.for_each(&mut |key, val| { + if let Some(key) = key.as_string() { + if let Some(val) = val.as_f64() { + result.insert(key, val as $num_ty); + } + } + }); + result + } + } + + impl IntoWasmable for HashMap { + fn into_wasm(self) -> Map { + let result = Map::new(); + for (key, val) in self { + result.set(&key.into(), &val.into()); + } + result + } + } + }; +} + +numbers_map! { f64 } +numbers_map! { f32 } + +numbers_map! { isize } +numbers_map! { usize } + +numbers_map! { i8 } +numbers_map! { i16 } +numbers_map! { i32 } +numbers_map! { i64 } +numbers_map! { i128 } + +numbers_map! { u8 } +numbers_map! { u16 } +numbers_map! { u32 } +numbers_map! { u64 } +numbers_map! { u128 } + +impl FromWasmable for HashMap { + fn from_wasm(js: Map) -> Self { + let mut result = HashMap::with_capacity(js.size() as usize); + js.for_each(&mut |key, val| { + if let Some(key) = key.as_string() { + if let Some(val) = val.as_string() { + result.insert(key, val); + } + } + }); + result + } +} + +impl IntoWasmable for HashMap { + fn into_wasm(self) -> Map { + let result = Map::new(); + for (key, val) in self { + result.set(&key.into(), &val.into()); + } + result + } +} + +impl FromWasmable for HashMap { + fn from_wasm(js: Map) -> Self { + let mut result = HashMap::with_capacity(js.size() as usize); + js.for_each(&mut |key, val| { + if let Some(key) = key.as_string() { + if let Some(val) = val.as_bool() { + result.insert(key, val); + } + } + }); + result + } +} + +impl IntoWasmable for HashMap { + fn into_wasm(self) -> Map { + let result = Map::new(); + for (key, val) in self { + result.set(&key.into(), &val.into()); + } + result + } +} diff --git a/usdpl-front/src/wasm/mod.rs b/usdpl-front/src/wasm/mod.rs new file mode 100644 index 0000000..6e9d7b1 --- /dev/null +++ b/usdpl-front/src/wasm/mod.rs @@ -0,0 +1,7 @@ +//! WASM <-> Rust interop utilities +mod arrays; +mod maps; +mod trivials; +mod wasm_traits; + +pub use wasm_traits::*; diff --git a/usdpl-front/src/wasm/trivials.rs b/usdpl-front/src/wasm/trivials.rs new file mode 100644 index 0000000..3187e26 --- /dev/null +++ b/usdpl-front/src/wasm/trivials.rs @@ -0,0 +1,40 @@ +use super::{FromWasmable, IntoWasmable}; + +macro_rules! trivial_convert { + ($ty: ty) => { + impl FromWasmable<$ty> for $ty { + fn from_wasm(js: $ty) -> Self { + js + } + } + + impl IntoWasmable<$ty> for $ty { + fn into_wasm(self) -> $ty { + self + } + } + }; +} + +trivial_convert! { f64 } +trivial_convert! { f32 } + +trivial_convert! { isize } +trivial_convert! { usize } + +trivial_convert! { i8 } +trivial_convert! { i16 } +trivial_convert! { i32 } +trivial_convert! { i64 } +trivial_convert! { i128 } + +trivial_convert! { u8 } +trivial_convert! { u16 } +trivial_convert! { u32 } +trivial_convert! { u64 } +trivial_convert! { u128 } + +trivial_convert! { bool } +trivial_convert! { String } + +trivial_convert! { () } diff --git a/usdpl-front/src/wasm/wasm_traits.rs b/usdpl-front/src/wasm/wasm_traits.rs new file mode 100644 index 0000000..ec1ec9f --- /dev/null +++ b/usdpl-front/src/wasm/wasm_traits.rs @@ -0,0 +1,40 @@ +/// A Rust type which supports Into/FromWasmAbi or WasmDescribe +pub trait KnownWasmCompatible {} + +/// Convert Rust type to WASM-compatible type +pub trait IntoWasmable { + /// Required method + fn into_wasm(self) -> T; +} + +/// Convert WASM-compatible type to Rust-centric type +pub trait FromWasmable { + /// Required method + fn from_wasm(js: T) -> Self; +} + +impl KnownWasmCompatible for f64 {} +impl KnownWasmCompatible for f32 {} + +impl KnownWasmCompatible for isize {} +impl KnownWasmCompatible for usize {} + +impl KnownWasmCompatible for i8 {} +impl KnownWasmCompatible for i16 {} +impl KnownWasmCompatible for i32 {} +impl KnownWasmCompatible for i64 {} +impl KnownWasmCompatible for i128 {} + +impl KnownWasmCompatible for u8 {} +impl KnownWasmCompatible for u16 {} +impl KnownWasmCompatible for u32 {} +impl KnownWasmCompatible for u64 {} +impl KnownWasmCompatible for u128 {} + +impl KnownWasmCompatible for bool {} +impl KnownWasmCompatible for String {} + +impl KnownWasmCompatible for () {} + +impl KnownWasmCompatible for js_sys::Map {} +impl KnownWasmCompatible for js_sys::Array {} From 4fed12d6d9eae40a41882c1818c5094324a655f0 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sun, 4 Jun 2023 22:22:28 -0400 Subject: [PATCH 05/12] Add error log message for service calls which return Err --- Cargo.lock | 1 + usdpl-build/src/front/service_generator.rs | 10 ++++++++-- usdpl-front/Cargo.toml | 1 + usdpl-front/src/lib.rs | 1 + 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e827963..42ea19a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1543,6 +1543,7 @@ dependencies = [ "gloo-net", "hex", "js-sys", + "log", "nrpc 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "obfstr", "prost", diff --git a/usdpl-build/src/front/service_generator.rs b/usdpl-build/src/front/service_generator.rs index 584fc45..4297795 100644 --- a/usdpl-build/src/front/service_generator.rs +++ b/usdpl-build/src/front/service_generator.rs @@ -24,6 +24,7 @@ fn generate_service_methods( ) -> proc_macro2::TokenStream { let mut gen_methods = Vec::with_capacity(service.methods.len()); for method in &service.methods { + let method_name_str = method.name.clone(); let method_name = quote::format_ident!("{}", method.name); let method_input = quote::format_ident!("{}{}", &service.name, method.input_type); let method_output = quote::format_ident!("{}{}Wasm", &service.name, method.output_type); @@ -88,8 +89,9 @@ fn generate_service_methods( let x2: #method_output_as_in = x.into(); Some(x2.into_wasm()) }, - Err(_e) => { - // TODO log error + Err(e) => { + // log error + log::error!("service:{}|method:{}|error:{}", self.service.descriptor(), #method_name_str, e); None } } @@ -468,6 +470,7 @@ impl ProtobufType { //"sfixed64" => quote::quote!{i64}, 8 => Self::Bool, 9 => Self::String, + 13 => Self::Uint32, //"bytes" => quote::quote!{Vec}, t => Self::Custom(format!("UnknownType{}", t)), } @@ -811,6 +814,9 @@ impl IServiceGenerator for WasmServiceGenerator { use usdpl_front::_helpers::wasm_bindgen; use usdpl_front::_helpers::wasm_bindgen_futures; use usdpl_front::_helpers::js_sys; + use usdpl_front::_helpers::log; + + use ::nrpc::ClientService; use usdpl_front::wasm::*; diff --git a/usdpl-front/Cargo.toml b/usdpl-front/Cargo.toml index a28ea0e..36ad139 100644 --- a/usdpl-front/Cargo.toml +++ b/usdpl-front/Cargo.toml @@ -47,6 +47,7 @@ hex = { version = "0.4", optional = true } nrpc = "0.6" usdpl-core = { version = "0.11", path = "../usdpl-core" } prost = "0.11" +log = "0.4" [dev-dependencies] wasm-bindgen-test = { version = "0.3.13" } diff --git a/usdpl-front/src/lib.rs b/usdpl-front/src/lib.rs index 96942aa..af5f3f6 100644 --- a/usdpl-front/src/lib.rs +++ b/usdpl-front/src/lib.rs @@ -22,6 +22,7 @@ pub mod _helpers { pub use js_sys; pub use wasm_bindgen; pub use wasm_bindgen_futures; + pub use log; } use std::sync::atomic::{AtomicU64, Ordering}; From 68b7455c9e6720dbbef36bf7697fb9240950f19e Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Wed, 28 Jun 2023 22:21:56 -0400 Subject: [PATCH 06/12] Make front WS service immutable to avoid wasm-rust exceptions, add debug logging --- Cargo.lock | 24 ++++++++++++++++------ usdpl-back/src/websockets.rs | 16 +++++++++++++-- usdpl-build/src/front/mod.rs | 15 ++++++++++++++ usdpl-build/src/front/service_generator.rs | 2 +- usdpl-front/Cargo.toml | 5 +++-- usdpl-front/src/client_handler.rs | 2 +- usdpl-front/src/lib.rs | 2 ++ 7 files changed, 54 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 42ea19a..44f28bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -183,6 +183,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "console_log" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f" +dependencies = [ + "log", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -797,6 +808,8 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "nrpc" version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f41caeb65399490c6f68ab2527a833d6f2e9b0d7d5ffc4b062f1484b3fa61cd" dependencies = [ "async-trait", "bytes", @@ -805,9 +818,7 @@ dependencies = [ [[package]] name = "nrpc" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f41caeb65399490c6f68ab2527a833d6f2e9b0d7d5ffc4b062f1484b3fa61cd" +version = "0.7.0" dependencies = [ "async-trait", "bytes", @@ -818,7 +829,7 @@ dependencies = [ name = "nrpc-build" version = "0.7.0" dependencies = [ - "nrpc 0.6.0", + "nrpc 0.7.0", "prettyplease 0.2.4", "proc-macro2", "prost-build", @@ -1502,7 +1513,7 @@ dependencies = [ "gettext-ng", "hex", "log", - "nrpc 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nrpc 0.6.0", "obfstr", "prost", "ratchet_rs", @@ -1539,12 +1550,13 @@ version = "0.11.0" dependencies = [ "async-channel", "console_error_panic_hook", + "console_log", "futures", "gloo-net", "hex", "js-sys", "log", - "nrpc 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nrpc 0.7.0", "obfstr", "prost", "usdpl-core", diff --git a/usdpl-back/src/websockets.rs b/usdpl-back/src/websockets.rs index 5fbed68..9b30695 100644 --- a/usdpl-back/src/websockets.rs +++ b/usdpl-back/src/websockets.rs @@ -46,7 +46,7 @@ impl WebsocketServer { let tcp = TcpListener::bind(addr).await?; while let Ok((stream, _addr_do_not_use)) = tcp.accept().await { - tokio::spawn(Self::connection_handler(self.services.clone(), stream)); + tokio::spawn(error_logger("USDPL websocket server error", Self::connection_handler(self.services.clone(), stream))); } Ok(()) @@ -65,6 +65,7 @@ impl WebsocketServer { services: ServiceRegistry<'static>, stream: TcpStream, ) -> Result<(), RatchetError> { + log::debug!("connection_handler invoked!"); let upgraded = ratchet_rs::accept_with( stream, WebSocketConfig::default(), @@ -77,6 +78,8 @@ impl WebsocketServer { let request_path = upgraded.request.uri().path(); + log::debug!("accepted new connection on uri {}", request_path); + let mut websocket = upgraded.websocket; let descriptor = Self::parse_uri_path(request_path) @@ -92,6 +95,7 @@ impl WebsocketServer { )) } Message::Binary => { + log::debug!("got binary ws message on uri {}", request_path); let response = services .call_descriptor( descriptor.service, @@ -102,6 +106,7 @@ impl WebsocketServer { .map_err(|e| { RatchetError::with_cause(ratchet_rs::ErrorKind::Protocol, e.to_string()) })?; + log::debug!("service completed response on uri {}", request_path); websocket.write_binary(response).await?; } Message::Ping(x) => websocket.write_pong(x).await?, @@ -109,11 +114,12 @@ impl WebsocketServer { Message::Close(_) => break, } } + log::debug!("ws connection {} closed", request_path); Ok(()) } fn parse_uri_path<'a>(path: &'a str) -> Result, &'static str> { - let mut iter = path.split('/'); + let mut iter = path.trim_matches('/').split('/'); if let Some(service) = iter.next() { if let Some(method) = iter.next() { if iter.next().is_none() { @@ -129,3 +135,9 @@ impl WebsocketServer { } } } + +async fn error_logger(msg: &'static str, f: impl core::future::Future>) { + if let Err(e) = f.await { + log::error!("{}: {}", msg, e); + } +} diff --git a/usdpl-build/src/front/mod.rs b/usdpl-build/src/front/mod.rs index b9a6f29..d706453 100644 --- a/usdpl-build/src/front/mod.rs +++ b/usdpl-build/src/front/mod.rs @@ -28,3 +28,18 @@ pub fn build( .transpile() .unwrap() } + +pub fn build_min( + custom_protos: impl Iterator, + custom_dirs: impl Iterator, +) { + crate::dump_protos_out().unwrap(); + nrpc_build::Transpiler::new( + crate::all_proto_filenames(crate::proto_builtins_out_path(), custom_protos), + crate::proto_out_paths(custom_dirs), + ) + .unwrap() + .generate_client() + .transpile() + .unwrap() +} diff --git a/usdpl-build/src/front/service_generator.rs b/usdpl-build/src/front/service_generator.rs index 4297795..ce6eeda 100644 --- a/usdpl-build/src/front/service_generator.rs +++ b/usdpl-build/src/front/service_generator.rs @@ -80,7 +80,7 @@ fn generate_service_methods( gen_methods.push(quote::quote! { #[wasm_bindgen] - pub async fn #method_name(&mut self, #(#input_params)*) -> Option<#method_output> { + pub async fn #method_name(&self, #(#input_params)*) -> Option<#method_output> { #params_to_fields_transformer diff --git a/usdpl-front/Cargo.toml b/usdpl-front/Cargo.toml index 36ad139..93d7970 100644 --- a/usdpl-front/Cargo.toml +++ b/usdpl-front/Cargo.toml @@ -14,7 +14,7 @@ crate-type = ["cdylib", "rlib"] [features] default = [] decky = ["usdpl-core/decky"] -debug = ["console_error_panic_hook"] +debug = ["console_error_panic_hook", "console_log"] encrypt = ["usdpl-core/encrypt", "obfstr", "hex"] [dependencies] @@ -22,6 +22,7 @@ wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" gloo-net = { version = "0.2", features = ["websocket"] } futures = "0.3" +console_log = { version = "1.0", optional = true, features = ["color"] } # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires @@ -44,7 +45,7 @@ async-channel = "1.8" obfstr = { version = "0.3", optional = true } hex = { version = "0.4", optional = true } -nrpc = "0.6" +nrpc = { version = "0.7", path = "../../nRPC/nrpc" } usdpl-core = { version = "0.11", path = "../usdpl-core" } prost = "0.11" log = "0.4" diff --git a/usdpl-front/src/client_handler.rs b/usdpl-front/src/client_handler.rs index 7d8fe94..74e0089 100644 --- a/usdpl-front/src/client_handler.rs +++ b/usdpl-front/src/client_handler.rs @@ -54,7 +54,7 @@ impl WebSocketHandler { #[async_trait::async_trait] impl ClientHandler for WebSocketHandler { async fn call( - &mut self, + &self, package: &str, service: &str, method: &str, diff --git a/usdpl-front/src/lib.rs b/usdpl-front/src/lib.rs index af5f3f6..db31334 100644 --- a/usdpl-front/src/lib.rs +++ b/usdpl-front/src/lib.rs @@ -78,6 +78,8 @@ fn increment_id() -> u64 { pub fn init_usdpl(port: u16) { #[cfg(feature = "console_error_panic_hook")] console_error_panic_hook::set_once(); + #[cfg(feature = "console_log")] + console_log::init_with_level(log::Level::Debug).expect("USDPL: error initializing console log"); //REMOTE_PORT.store(port, std::sync::atomic::Ordering::SeqCst); unsafe { CTX = UsdplContext { From 84cae5af7d7009ff7b7acc1fe1170903dc4a51d4 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sat, 22 Jul 2023 19:05:54 -0400 Subject: [PATCH 07/12] Add streaming method generation (untested) --- Cargo.lock | 517 ++++++++++----------- usdpl-back/Cargo.toml | 2 +- usdpl-build/Cargo.toml | 2 +- usdpl-build/src/front/service_generator.rs | 333 ++++++++++--- usdpl-front/Cargo.toml | 4 +- usdpl-front/src/client_handler.rs | 147 ++++-- usdpl-front/src/convert.rs | 8 + usdpl-front/src/lib.rs | 2 + usdpl-front/src/wasm/js_function_stream.rs | 74 +++ usdpl-front/src/wasm/mod.rs | 4 + usdpl-front/src/wasm/streaming.rs | 189 ++++++++ 11 files changed, 903 insertions(+), 379 deletions(-) create mode 100644 usdpl-front/src/wasm/js_function_stream.rs create mode 100644 usdpl-front/src/wasm/streaming.rs diff --git a/Cargo.lock b/Cargo.lock index 44f28bb..b8c205e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -45,16 +54,25 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.70" +name = "aho-corasick" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "async-channel" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", "event-listener", @@ -78,18 +96,18 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.25", ] [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.25", ] [[package]] @@ -98,6 +116,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.1" @@ -127,9 +160,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byteorder" @@ -202,9 +235,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "cpufeatures" -version = "0.2.6" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -220,9 +253,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -336,7 +369,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -372,9 +405,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "libz-sys", @@ -389,9 +422,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -452,7 +485,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.25", ] [[package]] @@ -497,9 +530,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -517,15 +550,22 @@ dependencies = [ ] [[package]] -name = "gloo-net" -version = "0.2.6" +name = "gimli" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "gloo-net" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3000ef231a67d5bfee6b35f2c0f6f5c8d45b3381ef5bbbea603690ec4e539762" dependencies = [ "futures-channel", "futures-core", "futures-sink", "gloo-utils", + "http", "js-sys", "pin-project", "serde", @@ -538,9 +578,9 @@ dependencies = [ [[package]] name = "gloo-utils" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" dependencies = [ "js-sys", "serde", @@ -563,18 +603,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -607,9 +638,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -636,13 +667,13 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -656,15 +687,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -677,15 +708,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.141" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libz-sys" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" dependencies = [ "cc", "pkg-config", @@ -694,15 +725,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.3.1" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -710,12 +741,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "logos" @@ -736,8 +764,8 @@ dependencies = [ "fnv", "proc-macro2", "quote", - "regex-syntax", - "syn 2.0.15", + "regex-syntax 0.6.29", + "syn 2.0.25", ] [[package]] @@ -757,9 +785,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "miette" -version = "5.7.0" +version = "5.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abdc09c381c9336b9f2e9bd6067a9a5290d20e2d2e2296f275456121c33ae89" +checksum = "a236ff270093b0b67451bc50a509bd1bad302cb1d3c7d37d5efe931238581fa9" dependencies = [ "miette-derive", "once_cell", @@ -769,34 +797,33 @@ dependencies = [ [[package]] name = "miette-derive" -version = "5.7.0" +version = "5.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8842972f23939443013dfd3720f46772b743e86f1a81d120d4b6fb090f87de1c" +checksum = "4901771e1d44ddb37964565c654a3223ba41a594d02b8da471cc4464912b5cfa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.25", ] [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -807,45 +834,35 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "nrpc" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f41caeb65399490c6f68ab2527a833d6f2e9b0d7d5ffc4b062f1484b3fa61cd" -dependencies = [ - "async-trait", - "bytes", - "prost", -] - -[[package]] -name = "nrpc" -version = "0.7.0" +version = "0.10.0" dependencies = [ "async-trait", "bytes", + "futures", "prost", ] [[package]] name = "nrpc-build" -version = "0.7.0" +version = "0.10.0" dependencies = [ - "nrpc 0.7.0", - "prettyplease 0.2.4", + "nrpc", + "prettyplease 0.2.10", "proc-macro2", "prost-build", "prost-types", "protox", "quote", - "syn 2.0.15", + "syn 2.0.25", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] @@ -856,10 +873,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b2b2cbbfd8defa51ff24450a61d73b3ff3e158484ddd274a883e886e6fbaa78" [[package]] -name = "once_cell" -version = "1.17.1" +name = "object" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -879,22 +905,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "smallvec", - "windows-sys 0.45.0", + "windows-targets", ] [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "petgraph" @@ -908,29 +934,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.25", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -940,9 +966,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "polyval" @@ -974,19 +1000,19 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.4" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" +checksum = "92139198957b410250d43fad93e630d956499a625c527eda65175c8680f83387" dependencies = [ "proc-macro2", - "syn 2.0.15", + "syn 2.0.25", ] [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" dependencies = [ "unicode-ident", ] @@ -1038,9 +1064,9 @@ dependencies = [ [[package]] name = "prost-reflect" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4df26814fb2cd59b1764305047aac8e9c467efe84a9e51bfd0c8811f08fe88a" +checksum = "000e1e05ebf7b26e1eba298e66fe4eee6eb19c567d0ffb35e0dd34231cdac4c8" dependencies = [ "logos", "miette", @@ -1060,9 +1086,9 @@ dependencies = [ [[package]] name = "protox" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cb2d1d74977bfa8fc4ddac1951ea639197c2896a830b6c5640085e498726da2" +checksum = "24022a7eb88547eaba87a1db7954c9c4cb4a143565c4e8f2b7f3e76eed63db2d" dependencies = [ "bytes", "miette", @@ -1075,9 +1101,9 @@ dependencies = [ [[package]] name = "protox-parse" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df78235a8c28dc4c5d8acb498d3000851b61b80a68774ff7b43323371b082ea4" +checksum = "712a2a651fa4466e67df6c967df5d7fc6cbffac89afc7b834f97ec49846c9c11" dependencies = [ "logos", "miette", @@ -1087,9 +1113,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] @@ -1184,15 +1210,6 @@ dependencies = [ "ratchet_ext", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - [[package]] name = "redox_syscall" version = "0.3.5" @@ -1204,11 +1221,25 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.3" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ - "regex-syntax", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax 0.7.3", +] + +[[package]] +name = "regex-automata" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.3", ] [[package]] @@ -1217,6 +1248,18 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "regex-syntax" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1228,23 +1271,23 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.11" +version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" [[package]] name = "scoped-tls" @@ -1266,15 +1309,15 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" dependencies = [ "itoa", "ryu", @@ -1314,9 +1357,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "socket2" @@ -1347,9 +1390,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" dependencies = [ "proc-macro2", "quote", @@ -1358,35 +1401,36 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.5.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ + "autocfg", "cfg-if", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall", "rustix", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.25", ] [[package]] @@ -1406,11 +1450,12 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", "mio", @@ -1420,18 +1465,18 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.25", ] [[package]] @@ -1462,9 +1507,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] name = "unicode-normalization" @@ -1493,9 +1538,9 @@ dependencies = [ [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -1513,7 +1558,7 @@ dependencies = [ "gettext-ng", "hex", "log", - "nrpc 0.6.0", + "nrpc", "obfstr", "prost", "ratchet_rs", @@ -1527,12 +1572,12 @@ name = "usdpl-build" version = "0.11.0" dependencies = [ "nrpc-build", - "prettyplease 0.2.4", + "prettyplease 0.2.10", "proc-macro2", "prost-build", "prost-types", "quote", - "syn 2.0.15", + "syn 2.0.25", ] [[package]] @@ -1556,7 +1601,7 @@ dependencies = [ "hex", "js-sys", "log", - "nrpc 0.7.0", + "nrpc", "obfstr", "prost", "usdpl-core", @@ -1586,9 +1631,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1596,24 +1641,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.25", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -1623,9 +1668,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1633,28 +1678,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.25", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-bindgen-test" -version = "0.3.34" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db36fc0f9fb209e88fb3642590ae0205bb5a56216dabd963ba15879fe53a30b" +checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" dependencies = [ "console_error_panic_hook", "js-sys", @@ -1666,9 +1711,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.34" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0734759ae6b3b1717d661fe4f016efcfb9828f5edb4520c18eaee05af3b43be9" +checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" dependencies = [ "proc-macro2", "quote", @@ -1676,9 +1721,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -1717,132 +1762,66 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" diff --git a/usdpl-back/Cargo.toml b/usdpl-back/Cargo.toml index 1037037..ff6e69d 100644 --- a/usdpl-back/Cargo.toml +++ b/usdpl-back/Cargo.toml @@ -19,7 +19,7 @@ usdpl-core = { version = "0.11", path = "../usdpl-core"} log = "0.4" # gRPC/protobuf -nrpc = "0.6" +nrpc = { version = "0.10", path = "../../nRPC/nrpc", default-features = false, features = [ "server-send" ] } async-lock = "2.7" prost = "0.11" diff --git a/usdpl-build/Cargo.toml b/usdpl-build/Cargo.toml index 9b1f1ad..796f836 100644 --- a/usdpl-build/Cargo.toml +++ b/usdpl-build/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -nrpc-build = { version = "0.7", path = "../../nRPC/nrpc-build" } +nrpc-build = { version = "0.10", path = "../../nRPC/nrpc-build" } prost-build = "0.11" prost-types = "0.11" diff --git a/usdpl-build/src/front/service_generator.rs b/usdpl-build/src/front/service_generator.rs index ce6eeda..00189f4 100644 --- a/usdpl-build/src/front/service_generator.rs +++ b/usdpl-build/src/front/service_generator.rs @@ -35,68 +35,203 @@ fn generate_service_methods( let mut input_params = Vec::with_capacity(input_type.field.len()); let mut params_to_fields = Vec::with_capacity(input_type.field.len()); - for field in &input_type.field { - //let param_name = quote::format_ident!("val{}", i.to_string()); - let type_enum = ProtobufType::from_field(field, &service.name, false); - //let rs_type_name = type_enum.to_tokens(); - let js_type_name = type_enum.to_wasm_tokens(); - let rs_type_name = type_enum.to_tokens(); - let field_name = quote::format_ident!( - "{}", - field - .name - .as_ref() - .expect("Protobuf message field needs a name") - ); - input_params.push(quote::quote! { - #field_name: #js_type_name, - }); - params_to_fields.push(quote::quote! { - #field_name: #rs_type_name::from_wasm(#field_name.into()),//: #field_name, - }); - } - let params_to_fields_transformer = if input_type.field.len() == 1 { - let field_name = quote::format_ident!( - "{}", - input_type.field[0] - .name - .as_ref() - .expect("Protobuf message field needs a name") - ); - quote::quote! { - let val = #method_input::from_wasm(#field_name.into()); - } - } else if input_type.field.is_empty() { - quote::quote! { - let val = #method_input {}; - } - } else { - quote::quote! { - let val = #method_input { - #(#params_to_fields)* - }; - } - }; - gen_methods.push(quote::quote! { - #[wasm_bindgen] - pub async fn #method_name(&self, #(#input_params)*) -> Option<#method_output> { - - #params_to_fields_transformer - - match self.service.#method_name(val.into()).await { - Ok(x) => { - let x2: #method_output_as_in = x.into(); - Some(x2.into_wasm()) - }, - Err(e) => { - // log error - log::error!("service:{}|method:{}|error:{}", self.service.descriptor(), #method_name_str, e); - None - } + match (method.client_streaming, method.server_streaming) { + (false, false) => { + for field in &input_type.field { + //let param_name = quote::format_ident!("val{}", i.to_string()); + let type_enum = ProtobufType::from_field(field, &service.name, false); + //let rs_type_name = type_enum.to_tokens(); + let js_type_name = type_enum.to_wasm_tokens(); + let rs_type_name = type_enum.to_tokens(); + let field_name = quote::format_ident!( + "{}", + field + .name + .as_ref() + .expect("Protobuf message field needs a name") + ); + input_params.push(quote::quote! { + #field_name: #js_type_name, + }); + params_to_fields.push(quote::quote! { + #field_name: #rs_type_name::from_wasm(#field_name.into()),//: #field_name, + }); } - } - }); + let params_to_fields_transformer = if input_type.field.len() == 1 { + let field_name = quote::format_ident!( + "{}", + input_type.field[0] + .name + .as_ref() + .expect("Protobuf message field needs a name") + ); + quote::quote! { + let val = #method_input::from_wasm(#field_name.into()); + } + } else if input_type.field.is_empty() { + quote::quote! { + let val = #method_input {}; + } + } else { + quote::quote! { + let val = #method_input { + #(#params_to_fields)* + }; + } + }; + + gen_methods.push(quote::quote! { + #[wasm_bindgen] + pub async fn #method_name(&self, #(#input_params)*) -> Option<#method_output> { + + #params_to_fields_transformer + + match self.service.#method_name(val.into()).await { + Ok(x) => { + let x2: #method_output_as_in = x.into(); + Some(x2.into_wasm()) + }, + Err(e) => { + // log error + log::error!("service:{}|method:{}|error:{}", self.service.descriptor(), #method_name_str, e); + None + } + } + } + }); + }, + (true, false) => { + // many -> 1 + gen_methods.push(quote::quote! { + #[wasm_bindgen] + pub async fn #method_name(&self, generator: js_sys::Function) -> Option<#method_output> { + + // function into Rust futures Stream + let stream = Box::new(::usdpl_front::wasm::JsFunctionStream::<#method_input>::from_function(generator)); + + match self.service.#method_name(stream).await { + Ok(x) => { + let x2: #method_output_as_in = x.into(); + Some(x2.into_wasm()) + }, + Err(e) => { + // log error + log::error!("service:{}|method:{}|error:{}", self.service.descriptor(), #method_name_str, e); + None + } + } + } + }); + }, + (false, true) => { + // 1 -> many + for field in &input_type.field { + //let param_name = quote::format_ident!("val{}", i.to_string()); + let type_enum = ProtobufType::from_field(field, &service.name, false); + //let rs_type_name = type_enum.to_tokens(); + let js_type_name = type_enum.to_wasm_tokens(); + let rs_type_name = type_enum.to_tokens(); + let field_name = quote::format_ident!( + "{}", + field + .name + .as_ref() + .expect("Protobuf message field needs a name") + ); + input_params.push(quote::quote! { + #field_name: #js_type_name, + }); + params_to_fields.push(quote::quote! { + #field_name: #rs_type_name::from_wasm(#field_name.into()),//: #field_name, + }); + } + let params_to_fields_transformer = if input_type.field.len() == 1 { + let field_name = quote::format_ident!( + "{}", + input_type.field[0] + .name + .as_ref() + .expect("Protobuf message field needs a name") + ); + quote::quote! { + let val = #method_input::from_wasm(#field_name.into()); + } + } else if input_type.field.is_empty() { + quote::quote! { + let val = #method_input {}; + } + } else { + quote::quote! { + let val = #method_input { + #(#params_to_fields)* + }; + } + }; + + gen_methods.push(quote::quote! { + #[wasm_bindgen] + pub async fn #method_name(&self, #(#input_params)*, callback: js_sys::Function) { + + #params_to_fields_transformer + + match self.service.#method_name(val.into()).await { + Ok(x) => { + while let Some(next_result) = x.next().await { + match next_result { + Err(e) => { + log::error!("service:{}|method:{}|error:{}", self.service.descriptor(), #method_name_str, e); + }, + Ok(item) => { + callback.call1(JsValue::undefined(), item.into_wasm_streamable()); + } + } + } + }, + Err(e) => { + // log error + log::error!("service:{}|method:{}|error:{}", self.service.descriptor(), #method_name_str, e); + } + } + } + }); + }, + (true, true) => { + // many -> many + gen_methods.push(quote::quote! { + #[wasm_bindgen] + pub async fn #method_name(&self, generator: js_sys::Function, callback: js_sys::Function) -> Option<#method_output> { + + // function into Rust futures Stream + let stream = Box::new(::usdpl_front::wasm::JsFunctionStream::<#method_input>::from_function(generator)); + + match self.service.#method_name(stream).await { + Ok(x) => { + while let Some(next_result) = x.next().await { + match next_result { + Err(e) => { + log::error!("service:{}|method:{}|error:{}", self.service.descriptor(), #method_name_str, e); + }, + Ok(item) => { + callback.call1(JsValue::undefined(), item.into_wasm_streamable()); + } + } + } + }, + Err(e) => { + // log error + log::error!("service:{}|method:{}|error:{}", self.service.descriptor(), #method_name_str, e); + None + } + } + } + }); + }, + } + + + + } quote::quote! { #(#gen_methods)* @@ -198,6 +333,7 @@ fn generate_wasm_struct_interop( .as_ref() .expect("Protobuf message needs a name") ); + let js_map_name = quote::format_ident!("{}", "js_map"); let mut gen_fields = Vec::with_capacity(descriptor.field.len()); let mut gen_into_fields = Vec::with_capacity(descriptor.field.len()); let mut gen_from_fields = Vec::with_capacity(descriptor.field.len()); @@ -288,17 +424,21 @@ fn generate_wasm_struct_interop( } else if descriptor.field.len() == 1 { let field = &descriptor.field[0]; //dbg!(descriptor, field); + let field_name_str = field + .name + .as_ref() + .expect("Protobuf message field needs a name"); let field_name = quote::format_ident!( "{}", - field - .name - .as_ref() - .expect("Protobuf message field needs a name") + field_name_str ); let type_enum = ProtobufType::from_field(field, service, is_known_map(field, known_maps)); let type_name = type_enum.to_tokens(); let wasm_type_name = type_enum.to_wasm_tokens(); + let into_wasm_streamable = quote::quote!{self.into_wasm_streamable()}; + let from_wasm_streamable = quote::quote!{#type_name::from_wasm_streamable(js)}; + quote::quote! { pub type #msg_name = #type_name; pub type #msg_name_wasm = #wasm_type_name; @@ -320,22 +460,41 @@ fn generate_wasm_struct_interop( } } + impl ::usdpl_front::wasm::FromWasmStreamableType for #msg_name { + fn from_wasm_streamable(js: JsValue) -> Result { + #from_wasm_streamable + } + } + + impl ::usdpl_front::wasm::IntoWasmStreamableType for #msg_name { + fn into_wasm_streamable(self) -> JsValue { + #into_wasm_streamable + } + } + #(#gen_nested_types)* #(#gen_enums)* } } else { + let mut gen_into_wasm_streamable_fields = Vec::with_capacity(descriptor.field.len()); + let mut gen_from_wasm_streamable_fields = Vec::with_capacity(descriptor.field.len()); + for field in &descriptor.field { + let field_name_str = field + .name + .as_ref() + .expect("Protobuf message field needs a name"); let field_name = quote::format_ident!( "{}", - field - .name - .as_ref() - .expect("Protobuf message field needs a name") + field_name_str ); let type_enum = ProtobufType::from_field(field, service, is_known_map(field, known_maps)); let type_name = type_enum.to_tokens(); + + let into_wasm_streamable = type_enum.to_into_wasm_streamable(field_name_str, &js_map_name); + let from_wasm_streamable = type_enum.to_from_wasm_streamable(field_name_str, &js_map_name); //let wasm_type_name = type_enum.to_wasm_tokens(); gen_fields.push(quote::quote! { pub #field_name: #type_name, @@ -347,6 +506,9 @@ fn generate_wasm_struct_interop( gen_from_fields.push(quote::quote! { #field_name: <_>::from(other.#field_name), }); + + gen_into_wasm_streamable_fields.push(into_wasm_streamable); + gen_from_wasm_streamable_fields.push(from_wasm_streamable); } let wasm_attribute_maybe = @@ -399,6 +561,23 @@ fn generate_wasm_struct_interop( } } + impl ::usdpl_front::wasm::FromWasmStreamableType for #msg_name { + fn from_wasm_streamable(js: JsValue) -> Result { + let #js_map_name = js_sys::Map::from(js); + Ok(Self { + #(#gen_from_wasm_streamable_fields)* + }) + } + } + + impl ::usdpl_front::wasm::IntoWasmStreamableType for #msg_name { + fn into_wasm_streamable(self) -> JsValue { + let #js_map_name = js_sys::Map::new(); + #(#gen_into_wasm_streamable_fields)* + #js_map_name.into() + } + } + #(#gen_nested_types)* #(#gen_enums)* @@ -551,6 +730,18 @@ impl ProtobufType { } } } + + fn to_into_wasm_streamable(&self, field_name: &str, js_map_name: &syn::Ident) -> proc_macro2::TokenStream { + //let type_tokens = self.to_tokens(); + //let field_ident = quote::format_ident!("{}", field_name); + quote::quote!{#js_map_name.set(#field_name.into(), self.field_ident);} + } + + fn to_from_wasm_streamable(&self, field_name: &str, js_map_name: &syn::Ident) -> proc_macro2::TokenStream { + let type_tokens = self.to_tokens(); + //let field_ident = quote::format_ident!("{}", field_name); + quote::quote!{#field_name: #type_tokens::from_wasm_streamable(#js_map_name.get(#field_name.into()))?,} + } } fn generate_wasm_enum_interop( @@ -815,8 +1006,10 @@ impl IServiceGenerator for WasmServiceGenerator { use usdpl_front::_helpers::wasm_bindgen_futures; use usdpl_front::_helpers::js_sys; use usdpl_front::_helpers::log; - - use ::nrpc::ClientService; + use usdpl_front::_helpers::futures; + use usdpl_front::_helpers::futures::StreamExt; + use usdpl_front::_helpers::nrpc::ClientService; + use usdpl_front::wasm::{IntoWasmStreamableType, FromWasmStreamableType}; use usdpl_front::wasm::*; diff --git a/usdpl-front/Cargo.toml b/usdpl-front/Cargo.toml index 93d7970..33a7877 100644 --- a/usdpl-front/Cargo.toml +++ b/usdpl-front/Cargo.toml @@ -20,7 +20,7 @@ encrypt = ["usdpl-core/encrypt", "obfstr", "hex"] [dependencies] wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" -gloo-net = { version = "0.2", features = ["websocket"] } +gloo-net = { version = "0.3", features = ["websocket"] } futures = "0.3" console_log = { version = "1.0", optional = true, features = ["color"] } @@ -45,7 +45,7 @@ async-channel = "1.8" obfstr = { version = "0.3", optional = true } hex = { version = "0.4", optional = true } -nrpc = { version = "0.7", path = "../../nRPC/nrpc" } +nrpc = { version = "0.10", path = "../../nRPC/nrpc", default-features = false} usdpl-core = { version = "0.11", path = "../usdpl-core" } prost = "0.11" log = "0.4" diff --git a/usdpl-front/src/client_handler.rs b/usdpl-front/src/client_handler.rs index 74e0089..9c3a703 100644 --- a/usdpl-front/src/client_handler.rs +++ b/usdpl-front/src/client_handler.rs @@ -1,8 +1,8 @@ use std::sync::atomic::{AtomicU64, Ordering}; -use futures::{SinkExt, StreamExt}; -use gloo_net::websocket::{futures::WebSocket, Message}; -use nrpc::{ClientHandler, ServiceError, _helpers::async_trait, _helpers::bytes}; +use futures::{SinkExt, StreamExt, future::{select, Either}}; +use gloo_net::websocket::{futures::WebSocket, Message, State}; +use nrpc::{ClientHandler, ServiceError, ServiceClientStream, _helpers::async_trait, _helpers::bytes}; use wasm_bindgen_futures::spawn_local; static LAST_ID: AtomicU64 = AtomicU64::new(0); @@ -13,24 +13,104 @@ pub struct WebSocketHandler { port: u16, } -async fn send_recv_ws(url: String, input: bytes::Bytes) -> Result, String> { - let mut ws = WebSocket::open(&url).map_err(|e| e.to_string())?; - ws.send(Message::Bytes(input.into())) - .await - .map_err(|e| e.to_string())?; - - read_next_incoming(ws).await -} - -async fn read_next_incoming(mut ws: WebSocket) -> Result, String> { - if let Some(msg) = ws.next().await { - match msg.map_err(|e| e.to_string())? { - Message::Bytes(b) => Ok(b), - Message::Text(_) => Err("Message::Text not allowed".into()), +async fn send_recv_ws<'a>(tx: async_channel::Sender>, url: String, mut input: ServiceClientStream<'a, bytes::Bytes>) { + let ws = match WebSocket::open(&url).map_err(|e| e.to_string()) { + Ok(x) => x, + Err(e) => { + tx.send(Err(e.to_string())).await.unwrap_or(()); + return; + } + }; + + let (mut input_done, mut output_done) = (false, false); + let mut last_ws_state = ws.state(); + let (mut ws_sink, mut ws_stream) = ws.split(); + let (mut left, mut right) = (input.next(), ws_stream.next()); + while let State::Open = last_ws_state { + if !input_done && !output_done { + match select(left, right).await { + Either::Left((next, outstanding)) => { + if let Some(next) = next { + match next { + Ok(next) => { + if let Err(e) = ws_sink.send(Message::Bytes(next.into())).await { + tx.send(Err(e.to_string())).await.unwrap_or(()); + } + }, + Err(e) => tx.send(Err(e.to_string())).await.unwrap_or(()) + } + } else { + input_done = true; + } + right = outstanding; + left = input.next(); + }, + Either::Right((response, outstanding)) => { + if let Some(next) = response { + match next { + Ok(Message::Bytes(b)) => tx.send(Ok(b.into())).await.unwrap_or(()), + Ok(_) => tx.send(Err("Message::Text not allowed".into())).await.unwrap_or(()), + Err(e) => tx.send(Err(e.to_string())).await.unwrap_or(()), + } + } else { + output_done = true; + } + left = outstanding; + let ws = ws_stream.reunite(ws_sink).unwrap(); + last_ws_state = ws.state(); + (ws_sink, ws_stream) = ws.split(); + right = ws_stream.next(); + } + } + } else if input_done { + if let Some(next) = right.await { + match next { + Ok(Message::Bytes(b)) => tx.send(Ok(b.into())).await.unwrap_or(()), + Ok(_) => tx.send(Err("Message::Text not allowed".into())).await.unwrap_or(()), + Err(e) => tx.send(Err(e.to_string())).await.unwrap_or(()), + } + } else { + output_done = true; + } + //left = outstanding; + let ws = ws_stream.reunite(ws_sink).unwrap(); + last_ws_state = ws.state(); + (ws_sink, ws_stream) = ws.split(); + right = ws_stream.next(); + } else { + } - } else { - Err("No response received".into()) } + /*spawn_local(async move { + while let State::Open = ws.state() { + if let Some(next) = input.next().await { + match next { + Ok(next) => { + if let Err(e) = ws.send(Message::Bytes(next.into())).await { + tx2.send(Err(e.to_string())).await.unwrap_or(()); + } + }, + Err(e) => tx2.send(Err(e.to_string())).await.unwrap_or(()) + } + } else { + break; + } + } + }); + + spawn_local(async move { + while let State::Open = ws.state() { + if let Some(next) = ws.next().await { + match next { + Ok(Message::Bytes(b)) => tx.send(Ok(b.into())).await.unwrap_or(()), + Ok(_) => tx.send(Err("Message::Text not allowed".into())).await.unwrap_or(()), + Err(e) => tx.send(Err(e.to_string())).await.unwrap_or(()), + } + } else { + break; + } + } + });*/ } #[derive(Debug)] @@ -44,6 +124,8 @@ impl std::fmt::Display for ErrorStr { impl std::error::Error for ErrorStr {} +const CHANNEL_BOUND: usize = 4; + impl WebSocketHandler { /// Instantiate the web socket client for connecting on the specified port pub fn new(port: u16) -> Self { @@ -51,32 +133,25 @@ impl WebSocketHandler { } } -#[async_trait::async_trait] -impl ClientHandler for WebSocketHandler { - async fn call( +#[async_trait::async_trait(?Send)] +impl ClientHandler<'static> for WebSocketHandler { + async fn call<'a: 'static>( &self, package: &str, service: &str, method: &str, - input: bytes::Bytes, - output: &mut bytes::BytesMut, - ) -> Result<(), ServiceError> { + input: ServiceClientStream<'a, bytes::Bytes>, + ) -> Result, ServiceError> { let id = LAST_ID.fetch_add(1, Ordering::SeqCst); let url = format!( "ws://usdpl-ws-{}.localhost:{}/{}.{}/{}", id, self.port, package, service, method, ); - let (tx, rx) = async_channel::bounded(1); - spawn_local(async move { - tx.send(send_recv_ws(url, input).await).await.unwrap_or(()); - }); + let (tx, rx) = async_channel::bounded(CHANNEL_BOUND); + spawn_local(send_recv_ws(tx, url, input)); - output.extend_from_slice( - &rx.recv() - .await - .map_err(|e| ServiceError::Method(Box::new(e)))? - .map_err(|e| ServiceError::Method(Box::new(ErrorStr(e))))?, - ); - Ok(()) + Ok(Box::new(rx.map(|buf_result: Result| buf_result + .map(|buf| bytes::Bytes::from(buf)) + .map_err(|e| ServiceError::Method(Box::new(ErrorStr(e))))))) } } diff --git a/usdpl-front/src/convert.rs b/usdpl-front/src/convert.rs index b074ae6..f0cc660 100644 --- a/usdpl-front/src/convert.rs +++ b/usdpl-front/src/convert.rs @@ -38,3 +38,11 @@ pub(crate) fn js_to_primitive(val: JsValue) -> Primitive { pub(crate) fn str_to_js(s: S) -> JsString { s.to_string().into() } + +pub(crate) fn js_to_str(js: JsValue) -> String { + if let Some(s) = js.as_string() { + s + } else { + format!("{:?}", js) + } +} diff --git a/usdpl-front/src/lib.rs b/usdpl-front/src/lib.rs index db31334..27a75f8 100644 --- a/usdpl-front/src/lib.rs +++ b/usdpl-front/src/lib.rs @@ -23,6 +23,8 @@ pub mod _helpers { pub use wasm_bindgen; pub use wasm_bindgen_futures; pub use log; + pub use futures; + pub use nrpc; } use std::sync::atomic::{AtomicU64, Ordering}; diff --git a/usdpl-front/src/wasm/js_function_stream.rs b/usdpl-front/src/wasm/js_function_stream.rs new file mode 100644 index 0000000..4bd5c02 --- /dev/null +++ b/usdpl-front/src/wasm/js_function_stream.rs @@ -0,0 +1,74 @@ +use core::pin::Pin; +use core::future::Future; + +use futures::{Stream, task::{Poll, Context}}; +use wasm_bindgen_futures::JsFuture; +use wasm_bindgen::JsValue; +use js_sys::{Function, Promise}; + +use nrpc::ServiceError; +use super::FromWasmStreamableType; +use crate::convert::js_to_str; + +/// futures::Stream wrapper for a JS async function that generates a new T-like value every call +pub struct JsFunctionStream { + function: Function, + promise: Option, + _idc: std::marker::PhantomData, +} + +impl JsFunctionStream { + /// Construct the function stream wrapper + pub fn from_function(f: Function) -> Self { + Self { + function: f, + promise: None, + _idc: std::marker::PhantomData::default(), + } + } +} + +impl Stream for JsFunctionStream { + type Item = Result; + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context<'_> + ) -> Poll> { + // this is horrible, I'm sorry + let js_poll = if let Some(mut promise) = self.promise.take() { + let mut pin = Pin::new(&mut promise); + JsFuture::poll(pin.as_mut(), cx) + } else { + let function_result = match self.function.call0(&JsValue::undefined()) { + Ok(x) => x, + Err(e) => return Poll::Ready(Some(Err(ServiceError::Method(s_to_err(format!("JS function call error: {}", js_to_str(e))))))) + }; + + let js_promise = Promise::from(function_result); + let mut js_future = JsFuture::from(js_promise); + let mut pin = Pin::new(&mut js_future); + let poll = JsFuture::poll(pin.as_mut(), cx); + self.promise = Some(js_future); + poll + }; + js_poll.map(|t| match t { + Ok(t) => { + if t.is_null() || t.is_undefined() { + None + } else { + Some(T::from_wasm_streamable(t).map_err(|e| ServiceError::Method(s_to_err(format!("JS type conversion error: {}", e))))) + } + }, + Err(e) => Some(Err(ServiceError::Method(s_to_err(format!("JS function promise error: {}", js_to_str(e)))))) + }) + } +} + +fn s_to_err(s: String) -> Box<(dyn std::error::Error + Send + Sync + 'static)> { + s.into() +} + +fn _check_service_stream(js_stream: JsFunctionStream) { + let _: nrpc::ServiceClientStream<'static, T> = Box::new(js_stream); +} diff --git a/usdpl-front/src/wasm/mod.rs b/usdpl-front/src/wasm/mod.rs index 6e9d7b1..17bf56b 100644 --- a/usdpl-front/src/wasm/mod.rs +++ b/usdpl-front/src/wasm/mod.rs @@ -1,7 +1,11 @@ //! WASM <-> Rust interop utilities mod arrays; +mod js_function_stream; mod maps; +mod streaming; mod trivials; mod wasm_traits; +pub use js_function_stream::JsFunctionStream; pub use wasm_traits::*; +pub use streaming::*; diff --git a/usdpl-front/src/wasm/streaming.rs b/usdpl-front/src/wasm/streaming.rs new file mode 100644 index 0000000..9eea695 --- /dev/null +++ b/usdpl-front/src/wasm/streaming.rs @@ -0,0 +1,189 @@ +use wasm_bindgen::JsValue; + +/// Convert Rust type to WASM-compatible type involved in nRPC streaming +pub trait IntoWasmStreamableType { + /// Required method + fn into_wasm_streamable(self) -> JsValue; +} + +#[derive(Debug)] +/// Conversion error from FromWasmStreamableType +pub enum WasmStreamableConversionError { + /// JSValue underlying type is incorrect + UnexpectedType { + /// Expected Javascript type + expected: JsType, + /// Actual Javascript type + got: JsType, + }, +} + +impl core::fmt::Display for WasmStreamableConversionError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::UnexpectedType { expected, got } => write!(f, "Unexpected type {}, expected {}", expected, got), + } + } +} + +impl std::error::Error for WasmStreamableConversionError {} + +/// Approximation of all possible JS types detectable through Wasm +#[allow(missing_docs)] +#[derive(Debug)] +pub enum JsType { + Number, + String, + Bool, + Array, + BigInt, + Function, + Symbol, + Undefined, + Null, + Object, + Unknown, +} + +impl core::fmt::Display for JsType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Number => write!(f, "number"), + Self::String => write!(f, "string"), + Self::Bool => write!(f, "boolean"), + Self::Array => write!(f, "array"), + Self::BigInt => write!(f, "bigint"), + Self::Function => write!(f, "function"), + Self::Symbol => write!(f, "symbol"), + Self::Undefined => write!(f, "undefined"), + Self::Null => write!(f, "null"), + Self::Object => write!(f, "object"), + Self::Unknown => write!(f, ""), + } + } +} + +impl JsType { + fn guess(js: &JsValue) -> JsType { + if js.as_f64().is_some() { + Self::Number + } else if js.as_string().is_some() { + Self::String + } else if js.as_bool().is_some() { + Self::Bool + } else if js.is_array() { + Self::Array + } else if js.is_bigint() { + Self::BigInt + } else if js.is_function() { + Self::Function + } else if js.is_symbol() { + Self::Symbol + } else if js.is_undefined() { + Self::Undefined + } else if js.is_null() { + Self::Null + } else if js.is_object() { + Self::Object + } else { + Self::Unknown + } + } +} + +/// Convert WASM-compatible type involved in nRPC streaming to Rust-centric type +pub trait FromWasmStreamableType: Sized { + /// Required method + fn from_wasm_streamable(js: JsValue) -> Result; +} + +macro_rules! trivial_convert_number { + ($ty: ty) => { + impl FromWasmStreamableType for $ty { + fn from_wasm_streamable(js: JsValue) -> Result { + if let Some(num) = js.as_f64() { + Ok(num as $ty) + } else { + Err(WasmStreamableConversionError::UnexpectedType { + expected: JsType::Number, + got: JsType::guess(&js), + }) + } + } + } + + impl IntoWasmStreamableType for $ty { + fn into_wasm_streamable(self) -> JsValue { + self.into() + } + } + }; +} + +trivial_convert_number! { f64 } +trivial_convert_number! { f32 } + +trivial_convert_number! { isize } +trivial_convert_number! { usize } + +trivial_convert_number! { i8 } +trivial_convert_number! { i16 } +trivial_convert_number! { i32 } +trivial_convert_number! { i64 } +trivial_convert_number! { i128 } + +trivial_convert_number! { u8 } +trivial_convert_number! { u16 } +trivial_convert_number! { u32 } +trivial_convert_number! { u64 } +trivial_convert_number! { u128 } + +impl FromWasmStreamableType for String { + fn from_wasm_streamable(js: JsValue) -> Result { + if let Some(s) = js.as_string() { + Ok(s) + } else { + Err(WasmStreamableConversionError::UnexpectedType { + expected: JsType::String, + got: JsType::guess(&js), + }) + } + } +} + +impl IntoWasmStreamableType for String { + fn into_wasm_streamable(self) -> JsValue { + self.into() + } +} + +impl FromWasmStreamableType for bool { + fn from_wasm_streamable(js: JsValue) -> Result { + if let Some(b) = js.as_bool() { + Ok(b) + } else { + Err(WasmStreamableConversionError::UnexpectedType { + expected: JsType::Bool, + got: JsType::guess(&js), + }) + } + } +} + +impl IntoWasmStreamableType for bool { + fn into_wasm_streamable(self) -> JsValue { + self.into() + } +} + +impl FromWasmStreamableType for () { + fn from_wasm_streamable(_js: JsValue) -> Result { + Ok(()) + } +} + +impl IntoWasmStreamableType for () { + fn into_wasm_streamable(self) -> JsValue { + JsValue::undefined() + } +} From 72c7f111e8d6fe82fd238eb7dd2e9b9018876f6b Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Tue, 29 Aug 2023 19:49:17 -0400 Subject: [PATCH 08/12] Fix some generation bugs to get Fantastic to compile --- Cargo.lock | 16 +++--- usdpl-back/Cargo.toml | 2 +- usdpl-back/src/rpc/mod.rs | 5 +- usdpl-back/src/rpc/registry.rs | 19 +++--- usdpl-back/src/rpc/websocket_stream.rs | 34 +++++++++++ usdpl-back/src/websockets.rs | 49 ++++++++++++---- usdpl-build/src/front/service_generator.rs | 67 ++++++++++++++++------ usdpl-front/src/wasm/streaming.rs | 5 +- 8 files changed, 148 insertions(+), 49 deletions(-) create mode 100644 usdpl-back/src/rpc/websocket_stream.rs diff --git a/Cargo.lock b/Cargo.lock index b8c205e..57d6178 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1152,9 +1152,9 @@ dependencies = [ [[package]] name = "ratchet_core" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854bf6632d9f5c7fa7f77cbc332f2b0a8dfb2acc36c3f351fc36bf40f2759728" +checksum = "faed301a9f297e8cd3617a2bc79ed17eefa88d5873ed08517c96628b48d1f386" dependencies = [ "base64", "bitflags", @@ -1176,9 +1176,9 @@ dependencies = [ [[package]] name = "ratchet_deflate" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b144cb23a76d810b25737f4b87943fdfd7772b423bdc15c2b3820849207adc" +checksum = "77238362df52f64482e0bd1c413d2d3d0e20052056ba4d88918ef2e962c86f11" dependencies = [ "bytes", "flate2", @@ -1190,9 +1190,9 @@ dependencies = [ [[package]] name = "ratchet_ext" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67f97bb0776d195720319a1e9f08fa343fe3f9f0b7ebf9d97d5926ce50b8e1ad" +checksum = "35f5bf3bd015a94b77730229e895e03af945627984ee5c4f95d40fd9227ea36b" dependencies = [ "bytes", "http", @@ -1201,9 +1201,9 @@ dependencies = [ [[package]] name = "ratchet_rs" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7dba456fc23026b46ce0936d109ce3e73b4a592baf0dda0f83d49886c5e5f83" +checksum = "62d326d7cd4227a7f58b36c1efb16b348f7e2e43e1d1ef032e9b094ff6cec583" dependencies = [ "ratchet_core", "ratchet_deflate", diff --git a/usdpl-back/Cargo.toml b/usdpl-back/Cargo.toml index ff6e69d..872c61d 100644 --- a/usdpl-back/Cargo.toml +++ b/usdpl-back/Cargo.toml @@ -24,7 +24,7 @@ async-lock = "2.7" prost = "0.11" # websocket framework -ratchet_rs = { version = "0.3", features = [ "deflate" ] } +ratchet_rs = { version = "0.4", features = [ "deflate" ] } # HTTP web framework #warp = { version = "0.3" } diff --git a/usdpl-back/src/rpc/mod.rs b/usdpl-back/src/rpc/mod.rs index 6dddc35..506bfd6 100644 --- a/usdpl-back/src/rpc/mod.rs +++ b/usdpl-back/src/rpc/mod.rs @@ -1,2 +1,5 @@ mod registry; -pub use registry::ServiceRegistry; +pub use registry::{ServiceRegistry, StaticServiceRegistry}; + +mod websocket_stream; +pub use websocket_stream::ws_stream; diff --git a/usdpl-back/src/rpc/registry.rs b/usdpl-back/src/rpc/registry.rs index 0560696..26cd152 100644 --- a/usdpl-back/src/rpc/registry.rs +++ b/usdpl-back/src/rpc/registry.rs @@ -2,11 +2,13 @@ use async_lock::Mutex; use std::collections::HashMap; use std::sync::Arc; -use nrpc::{ServerService, ServiceError}; +use nrpc::{ServerService, ServiceError, ServiceServerStream}; + +pub type StaticServiceRegistry = ServiceRegistry<'static>; #[derive(Default, Clone)] pub struct ServiceRegistry<'a> { - entries: HashMap>>>, + entries: HashMap + Send + 'a>>>>, } impl<'a> ServiceRegistry<'a> { @@ -19,23 +21,22 @@ impl<'a> ServiceRegistry<'a> { format!("{}.{}", package, service) }*/ - pub async fn call_descriptor( - &self, + pub async fn call_descriptor<'b: 'a>( + &mut self, descriptor: &str, method: &str, - data: bytes::Bytes, - ) -> Result { + input: ServiceServerStream<'a, bytes::Bytes>, + ) -> Result, ServiceError> { if let Some(service) = self.entries.get(descriptor) { - let mut output = bytes::BytesMut::new(); let mut service_lock = service.lock_arc().await; - service_lock.call(method, data, &mut output).await?; + let output = service_lock.call(method, input).await?; Ok(output.into()) } else { Err(ServiceError::ServiceNotFound) } } - pub fn register(&mut self, service: S) -> &mut Self { + pub fn register + Send + 'a>(&mut self, service: S) -> &mut Self { let key = service.descriptor().to_owned(); self.entries .insert(key, Arc::new(Mutex::new(Box::new(service)))); diff --git a/usdpl-back/src/rpc/websocket_stream.rs b/usdpl-back/src/rpc/websocket_stream.rs new file mode 100644 index 0000000..ea44c00 --- /dev/null +++ b/usdpl-back/src/rpc/websocket_stream.rs @@ -0,0 +1,34 @@ +use core::marker::Unpin; +use std::sync::Arc; + +use tokio::{net::TcpStream, sync::Mutex}; +use ratchet_rs::{WebSocket, Message, Error as RatchetError, Extension}; + +use nrpc::ServiceError; +use nrpc::_helpers::futures::Stream; +use nrpc::_helpers::bytes::{BytesMut, Bytes}; + +struct WsStreamState{ + ws: Arc>>, + buf: BytesMut, +} + +pub fn ws_stream<'a, T: Extension + Unpin + 'a>(ws: Arc>>) -> impl Stream> + 'a { + nrpc::_helpers::futures::stream::unfold(WsStreamState { ws, buf: BytesMut::new() }, |mut state| async move { + let mut locked_ws = state.ws.lock().await; + if locked_ws.is_closed() || !locked_ws.is_active() { + None + } else { + let result = locked_ws.read(&mut state.buf).await; + drop(locked_ws); + match result { + Ok(Message::Binary) => Some((Ok(state.buf.clone().freeze()), state)), + Ok(_) => Some((Err(ServiceError::Method(Box::new(RatchetError::with_cause( + ratchet_rs::ErrorKind::Protocol, + "Websocket text messages are not accepted", + )))), state)), + Err(e) => Some((Err(ServiceError::Method(Box::new(e))), state)) + } + } + }) +} diff --git a/usdpl-back/src/websockets.rs b/usdpl-back/src/websockets.rs index 9b30695..9658e2c 100644 --- a/usdpl-back/src/websockets.rs +++ b/usdpl-back/src/websockets.rs @@ -1,9 +1,10 @@ -use bytes::BytesMut; use ratchet_rs::deflate::DeflateExtProvider; -use ratchet_rs::{Error as RatchetError, Message, ProtocolRegistry, WebSocketConfig}; +use ratchet_rs::{Error as RatchetError, ProtocolRegistry, WebSocketConfig}; use tokio::net::{TcpListener, TcpStream}; -use crate::rpc::ServiceRegistry; +use nrpc::_helpers::futures::StreamExt; + +use crate::rpc::StaticServiceRegistry; struct MethodDescriptor<'a> { service: &'a str, @@ -12,7 +13,7 @@ struct MethodDescriptor<'a> { /// Handler for communication to and from the front-end pub struct WebsocketServer { - services: ServiceRegistry<'static>, + services: StaticServiceRegistry, port: u16, } @@ -20,18 +21,18 @@ impl WebsocketServer { /// Initialise an instance of the back-end websocket server pub fn new(port_usdpl: u16) -> Self { Self { - services: ServiceRegistry::new(), + services: StaticServiceRegistry::new(), port: port_usdpl, } } /// Get the service registry that the server handles - pub fn registry(&mut self) -> &'_ mut ServiceRegistry<'static> { + pub fn registry(&mut self) -> &'_ mut StaticServiceRegistry { &mut self.services } /// Register a nRPC service for this server to handle - pub fn register(mut self, service: S) -> Self { + pub fn register + Send + 'static>(mut self, service: S) -> Self { self.services.register(service); self } @@ -62,7 +63,7 @@ impl WebsocketServer { } async fn connection_handler( - services: ServiceRegistry<'static>, + mut services: StaticServiceRegistry, stream: TcpStream, ) -> Result<(), RatchetError> { log::debug!("connection_handler invoked!"); @@ -80,12 +81,38 @@ impl WebsocketServer { log::debug!("accepted new connection on uri {}", request_path); - let mut websocket = upgraded.websocket; + let websocket = std::sync::Arc::new(tokio::sync::Mutex::new(upgraded.websocket)); let descriptor = Self::parse_uri_path(request_path) .map_err(|e| RatchetError::with_cause(ratchet_rs::ErrorKind::Protocol, e))?; - let mut buf = BytesMut::new(); + let input_stream = Box::new(nrpc::_helpers::futures::stream::StreamExt::boxed(crate::rpc::ws_stream(websocket.clone()))); + let output_stream = services + .call_descriptor( + descriptor.service, + descriptor.method, + input_stream, + ) + .await + .map_err(|e| { + RatchetError::with_cause(ratchet_rs::ErrorKind::Protocol, e.to_string()) + })?; + + output_stream.for_each_concurrent(None, |result| async { + match result { + Ok(msg) => { + let mut ws_lock = websocket.lock().await; + if let Err(e) = ws_lock.write_binary(msg).await { + log::error!("websocket error while writing response on uri {}: {}", request_path, e); + } + }, + Err(e) => { + log::error!("service error while writing response on uri {}: {}", request_path, e); + } + } + }).await; + + /*let mut buf = BytesMut::new(); loop { match websocket.read(&mut buf).await? { Message::Text => { @@ -113,7 +140,7 @@ impl WebsocketServer { Message::Pong(_) => {} Message::Close(_) => break, } - } + }*/ log::debug!("ws connection {} closed", request_path); Ok(()) } diff --git a/usdpl-build/src/front/service_generator.rs b/usdpl-build/src/front/service_generator.rs index 00189f4..5391926 100644 --- a/usdpl-build/src/front/service_generator.rs +++ b/usdpl-build/src/front/service_generator.rs @@ -436,8 +436,26 @@ fn generate_wasm_struct_interop( let type_name = type_enum.to_tokens(); let wasm_type_name = type_enum.to_wasm_tokens(); - let into_wasm_streamable = quote::quote!{self.into_wasm_streamable()}; - let from_wasm_streamable = quote::quote!{#type_name::from_wasm_streamable(js)}; + /*let wasm_streamable_impl = if type_enum.is_already_wasm_streamable() { + quote::quote!{} + } else { + let into_wasm_streamable = quote::quote!{self.into_wasm_streamable()}; + let from_wasm_streamable = quote::quote!{#type_name::from_wasm_streamable(js)}; + quote::quote!{ + impl ::usdpl_front::wasm::FromWasmStreamableType for #msg_name { + fn from_wasm_streamable(js: JsValue) -> Result { + #from_wasm_streamable + } + } + + impl ::usdpl_front::wasm::IntoWasmStreamableType for #msg_name { + fn into_wasm_streamable(self) -> JsValue { + #into_wasm_streamable + } + } + } + };*/ + quote::quote! { pub type #msg_name = #type_name; @@ -460,17 +478,7 @@ fn generate_wasm_struct_interop( } } - impl ::usdpl_front::wasm::FromWasmStreamableType for #msg_name { - fn from_wasm_streamable(js: JsValue) -> Result { - #from_wasm_streamable - } - } - - impl ::usdpl_front::wasm::IntoWasmStreamableType for #msg_name { - fn into_wasm_streamable(self) -> JsValue { - #into_wasm_streamable - } - } + // #wasm_streamable_impl #(#gen_nested_types)* @@ -733,15 +741,19 @@ impl ProtobufType { fn to_into_wasm_streamable(&self, field_name: &str, js_map_name: &syn::Ident) -> proc_macro2::TokenStream { //let type_tokens = self.to_tokens(); - //let field_ident = quote::format_ident!("{}", field_name); - quote::quote!{#js_map_name.set(#field_name.into(), self.field_ident);} + let field_ident = quote::format_ident!("{}", field_name); + quote::quote!{#js_map_name.set(&JsValue::from(#field_name), &self.#field_ident.into_wasm_streamable());} } fn to_from_wasm_streamable(&self, field_name: &str, js_map_name: &syn::Ident) -> proc_macro2::TokenStream { let type_tokens = self.to_tokens(); - //let field_ident = quote::format_ident!("{}", field_name); - quote::quote!{#field_name: #type_tokens::from_wasm_streamable(#js_map_name.get(#field_name.into()))?,} + let field_ident = quote::format_ident!("{}", field_name); + quote::quote!{#field_ident: #type_tokens::from_wasm_streamable(#js_map_name.get(&JsValue::from(#field_name)))?,} } + + /*fn is_already_wasm_streamable(&self) -> bool { + !matches!(self, Self::Custom(_)) + }*/ } fn generate_wasm_enum_interop( @@ -870,6 +882,25 @@ fn generate_wasm_enum_interop( self as i32 } } + + impl ::usdpl_front::wasm::FromWasmStreamableType for #enum_name { + fn from_wasm_streamable(js: JsValue) -> Result { + if let Some(float) = js.as_f64() { + Ok(Self::from_wasm(float as i32)) + } else { + Err(::usdpl_front::wasm::WasmStreamableConversionError::UnexpectedType { + expected: ::usdpl_front::wasm::JsType::Number, + got: ::usdpl_front::wasm::JsType::guess(&js), + }) + } + } + } + + impl ::usdpl_front::wasm::IntoWasmStreamableType for #enum_name { + fn into_wasm_streamable(self) -> JsValue { + JsValue::from(self.into_wasm()) + } + } } } @@ -1021,7 +1052,7 @@ impl IServiceGenerator for WasmServiceGenerator { #[wasm_bindgen] pub struct #service_js_name { //#[wasm_bindgen(skip)] - service: super::#service_struct_name, + service: super::#service_struct_name<'static, WebSocketHandler>, } #[wasm_bindgen] diff --git a/usdpl-front/src/wasm/streaming.rs b/usdpl-front/src/wasm/streaming.rs index 9eea695..6c18ff8 100644 --- a/usdpl-front/src/wasm/streaming.rs +++ b/usdpl-front/src/wasm/streaming.rs @@ -64,7 +64,10 @@ impl core::fmt::Display for JsType { } impl JsType { - fn guess(js: &JsValue) -> JsType { + /// Guess the JS type of the parameter. + /// This is not guaranteed to be correct, but is intended to give more information + /// in debug and error messages + pub fn guess(js: &JsValue) -> JsType { if js.as_f64().is_some() { Self::Number } else if js.as_string().is_some() { From b7b42a8c6d1a7904e15c2ad93154df7ac8e51d3d Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Fri, 1 Sep 2023 18:04:34 -0400 Subject: [PATCH 09/12] Fix websocket communication front logic closing too early --- Cargo.lock | 39 +++---------------- Cargo.toml | 2 + usdpl-back/src/websockets.rs | 7 +++- usdpl-front/Cargo.toml | 6 +-- usdpl-front/src/client_handler.rs | 64 ++++++++++++++++++++++++++++--- usdpl-front/src/convert.rs | 14 +++---- usdpl-front/src/imports.rs | 16 -------- usdpl-front/src/lib.rs | 53 ++++++++++++++----------- 8 files changed, 111 insertions(+), 90 deletions(-) delete mode 100644 usdpl-front/src/imports.rs diff --git a/Cargo.lock b/Cargo.lock index 57d6178..b70aa29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,17 +68,6 @@ version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - [[package]] name = "async-lock" version = "2.7.0" @@ -197,15 +186,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "concurrent-queue" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -251,15 +231,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - [[package]] name = "ctr" version = "0.8.0" @@ -557,9 +528,9 @@ checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "gloo-net" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3000ef231a67d5bfee6b35f2c0f6f5c8d45b3381ef5bbbea603690ec4e539762" +checksum = "8ac9e8288ae2c632fa9f8657ac70bfe38a1530f345282d7ba66a1f70b72b7dc4" dependencies = [ "futures-channel", "futures-core", @@ -578,9 +549,9 @@ dependencies = [ [[package]] name = "gloo-utils" -version = "0.1.7" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" dependencies = [ "js-sys", "serde", @@ -1593,10 +1564,10 @@ dependencies = [ name = "usdpl-front" version = "0.11.0" dependencies = [ - "async-channel", "console_error_panic_hook", "console_log", "futures", + "futures-channel", "gloo-net", "hex", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index 4c8660f..52e25b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,8 @@ exclude = [ "templates/decky/backend" ] +resolver = "2" + [profile.release] # Tell `rustc` to optimize for small code size. opt-level = "s" diff --git a/usdpl-back/src/websockets.rs b/usdpl-back/src/websockets.rs index 9658e2c..7be05d0 100644 --- a/usdpl-back/src/websockets.rs +++ b/usdpl-back/src/websockets.rs @@ -98,7 +98,7 @@ impl WebsocketServer { RatchetError::with_cause(ratchet_rs::ErrorKind::Protocol, e.to_string()) })?; - output_stream.for_each_concurrent(None, |result| async { + output_stream.for_each(|result| async { match result { Ok(msg) => { let mut ws_lock = websocket.lock().await; @@ -112,6 +112,11 @@ impl WebsocketServer { } }).await; + websocket.lock().await.close(ratchet_rs::CloseReason { + code: ratchet_rs::CloseCode::Normal, + description: None, + }).await?; + /*let mut buf = BytesMut::new(); loop { match websocket.read(&mut buf).await? { diff --git a/usdpl-front/Cargo.toml b/usdpl-front/Cargo.toml index 33a7877..942c526 100644 --- a/usdpl-front/Cargo.toml +++ b/usdpl-front/Cargo.toml @@ -20,8 +20,9 @@ encrypt = ["usdpl-core/encrypt", "obfstr", "hex"] [dependencies] wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" -gloo-net = { version = "0.3", features = ["websocket"] } +gloo-net = { version = "0.4", features = ["websocket"] } futures = "0.3" +futures-channel = "0.3" console_log = { version = "1.0", optional = true, features = ["color"] } # The `console_error_panic_hook` crate provides better debugging of panics by @@ -37,11 +38,10 @@ web-sys = { version = "0.3", features = [ 'RequestMode', 'Response', 'Window', + 'console', ]} js-sys = { version = "0.3" } -async-channel = "1.8" - obfstr = { version = "0.3", optional = true } hex = { version = "0.4", optional = true } diff --git a/usdpl-front/src/client_handler.rs b/usdpl-front/src/client_handler.rs index 9c3a703..4f3c799 100644 --- a/usdpl-front/src/client_handler.rs +++ b/usdpl-front/src/client_handler.rs @@ -1,7 +1,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; use futures::{SinkExt, StreamExt, future::{select, Either}}; -use gloo_net::websocket::{futures::WebSocket, Message, State}; +use gloo_net::websocket::{futures::WebSocket, Message}; use nrpc::{ClientHandler, ServiceError, ServiceClientStream, _helpers::async_trait, _helpers::bytes}; use wasm_bindgen_futures::spawn_local; @@ -13,23 +13,42 @@ pub struct WebSocketHandler { port: u16, } -async fn send_recv_ws<'a>(tx: async_channel::Sender>, url: String, mut input: ServiceClientStream<'a, bytes::Bytes>) { - let ws = match WebSocket::open(&url).map_err(|e| e.to_string()) { +#[inline] +fn ws_is_alive(ws_state: &gloo_net::websocket::State) -> bool { + match ws_state { + gloo_net::websocket::State::Connecting | gloo_net::websocket::State::Open => true, + gloo_net::websocket::State::Closing | gloo_net::websocket::State::Closed => false, + } +} + +async fn send_recv_ws<'a>(mut tx: futures_channel::mpsc::Sender>, url: String, mut input: ServiceClientStream<'a, bytes::Bytes>) { + let ws = match WebSocket::open_with_protocol(&url, "usdpl-nrpc").map_err(|e| e.to_string()) { Ok(x) => x, Err(e) => { + log::error!("ws open error: {}", e); tx.send(Err(e.to_string())).await.unwrap_or(()); return; } }; + + #[cfg(feature = "debug")] + web_sys::console::log_1(&format!("ws opened successfully with url `{}`", url).into()); + let (mut input_done, mut output_done) = (false, false); let mut last_ws_state = ws.state(); + #[cfg(feature = "debug")] + web_sys::console::log_1(&format!("ws with url `{}` initial state: {:?}", url, last_ws_state).into()); let (mut ws_sink, mut ws_stream) = ws.split(); let (mut left, mut right) = (input.next(), ws_stream.next()); - while let State::Open = last_ws_state { + while ws_is_alive(&last_ws_state) { if !input_done && !output_done { + #[cfg(feature = "debug")] + web_sys::console::debug_1(&format!("Input and output streams are both alive").into()); match select(left, right).await { Either::Left((next, outstanding)) => { + #[cfg(feature = "debug")] + web_sys::console::debug_1(&format!("Got message to send over websocket").into()); if let Some(next) = next { match next { Ok(next) => { @@ -46,6 +65,8 @@ async fn send_recv_ws<'a>(tx: async_channel::Sender left = input.next(); }, Either::Right((response, outstanding)) => { + #[cfg(feature = "debug")] + web_sys::console::debug_1(&format!("Received message from websocket").into()); if let Some(next) = response { match next { Ok(Message::Bytes(b)) => tx.send(Ok(b.into())).await.unwrap_or(()), @@ -63,7 +84,11 @@ async fn send_recv_ws<'a>(tx: async_channel::Sender } } } else if input_done { + #[cfg(feature = "debug")] + web_sys::console::debug_1(&format!("Input stream is complete").into()); if let Some(next) = right.await { + #[cfg(feature = "debug")] + web_sys::console::debug_1(&format!("Received message from websocket").into()); match next { Ok(Message::Bytes(b)) => tx.send(Ok(b.into())).await.unwrap_or(()), Ok(_) => tx.send(Err("Message::Text not allowed".into())).await.unwrap_or(()), @@ -78,9 +103,34 @@ async fn send_recv_ws<'a>(tx: async_channel::Sender (ws_sink, ws_stream) = ws.split(); right = ws_stream.next(); } else { - + // output_done is true + #[cfg(feature = "debug")] + web_sys::console::debug_1(&format!("Output stream is complete").into()); + if let Some(next) = left.await { + #[cfg(feature = "debug")] + web_sys::console::debug_1(&format!("Got message to send over websocket").into()); + match next { + Ok(next) => { + if let Err(e) = ws_sink.send(Message::Bytes(next.into())).await { + tx.send(Err(e.to_string())).await.unwrap_or(()); + } + }, + Err(e) => tx.send(Err(e.to_string())).await.unwrap_or(()) + } + } else { + input_done = true; + } + //right = outstanding; + let ws = ws_stream.reunite(ws_sink).unwrap(); + last_ws_state = ws.state(); + (ws_sink, ws_stream) = ws.split(); + left = input.next(); + right = ws_stream.next(); // this should always resolve to None (but compiler is unhappy without this) } } + + #[cfg(feature = "debug")] + web_sys::console::debug_1(&format!("ws with url `{}` has closed", url).into()); /*spawn_local(async move { while let State::Open = ws.state() { if let Some(next) = input.next().await { @@ -147,7 +197,9 @@ impl ClientHandler<'static> for WebSocketHandler { "ws://usdpl-ws-{}.localhost:{}/{}.{}/{}", id, self.port, package, service, method, ); - let (tx, rx) = async_channel::bounded(CHANNEL_BOUND); + #[cfg(feature = "debug")] + web_sys::console::log_1(&format!("doing send/receive on ws url `{}`", url).into()); + let (tx, rx) = futures_channel::mpsc::channel(CHANNEL_BOUND); spawn_local(send_recv_ws(tx, url, input)); Ok(Box::new(rx.map(|buf_result: Result| buf_result diff --git a/usdpl-front/src/convert.rs b/usdpl-front/src/convert.rs index f0cc660..a59e6fa 100644 --- a/usdpl-front/src/convert.rs +++ b/usdpl-front/src/convert.rs @@ -1,10 +1,10 @@ -use js_sys::JsString; -use js_sys::JSON::{parse, stringify}; +//use js_sys::JsString; +//use js_sys::JSON::{parse, stringify}; use wasm_bindgen::prelude::JsValue; -use usdpl_core::serdes::Primitive; +//use usdpl_core::serdes::Primitive; -pub(crate) fn primitive_to_js(primitive: Primitive) -> JsValue { +/*pub(crate) fn primitive_to_js(primitive: Primitive) -> JsValue { match primitive { Primitive::Empty => JsValue::null(), Primitive::String(s) => JsValue::from_str(&s), @@ -33,11 +33,11 @@ pub(crate) fn js_to_primitive(val: JsValue) -> Primitive { } else { Primitive::Empty } -} +}*/ -pub(crate) fn str_to_js(s: S) -> JsString { +/*pub(crate) fn str_to_js(s: S) -> JsString { s.to_string().into() -} +}*/ pub(crate) fn js_to_str(js: JsValue) -> String { if let Some(s) = js.as_string() { diff --git a/usdpl-front/src/imports.rs b/usdpl-front/src/imports.rs deleted file mode 100644 index 8e13b48..0000000 --- a/usdpl-front/src/imports.rs +++ /dev/null @@ -1,16 +0,0 @@ -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -extern "C" { - #[cfg(feature = "debug")] - #[wasm_bindgen(js_namespace = console, js_name = log)] - pub fn console_log(s: &str); - - #[cfg(feature = "debug")] - #[wasm_bindgen(js_namespace = console, js_name = warn)] - pub fn console_warn(s: &str); - - #[cfg(feature = "debug")] - #[wasm_bindgen(js_namespace = console, js_name = error)] - pub fn console_error(s: &str); -} diff --git a/usdpl-front/src/lib.rs b/usdpl-front/src/lib.rs index 27a75f8..aa2fd5d 100644 --- a/usdpl-front/src/lib.rs +++ b/usdpl-front/src/lib.rs @@ -7,9 +7,8 @@ mod client_handler; pub use client_handler::WebSocketHandler; -mod connection; +//mod connection; mod convert; -mod imports; pub mod wasm; /*#[allow(missing_docs)] // existence is pain otherwise @@ -27,21 +26,21 @@ pub mod _helpers { pub use nrpc; } -use std::sync::atomic::{AtomicU64, Ordering}; +//use std::sync::atomic::{AtomicU64, Ordering}; -use js_sys::Array; +//use js_sys::Array; use wasm_bindgen::prelude::*; -use usdpl_core::{socket::Packet, RemoteCall}; +//use usdpl_core::{socket::Packet, RemoteCall}; //const REMOTE_CALL_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); //const REMOTE_PORT: std::sync::atomic::AtomicU16 = std::sync::atomic::AtomicU16::new(31337); -static mut CTX: UsdplContext = UsdplContext { +/*static mut CTX: UsdplContext = UsdplContext { port: 0, - id: AtomicU64::new(0), + //id: AtomicU64::new(0), #[cfg(feature = "encrypt")] key: Vec::new(), -}; +};*/ static mut CACHE: Option> = None; @@ -52,6 +51,7 @@ fn encryption_key() -> Vec { hex::decode(obfstr::obfstr!(env!("USDPL_ENCRYPTION_KEY"))).unwrap() } +/* //#[wasm_bindgen] #[derive(Debug)] struct UsdplContext { @@ -68,33 +68,39 @@ fn get_port() -> u16 { #[cfg(feature = "encrypt")] fn get_key() -> Vec { unsafe { CTX.key.clone() } -} +}*/ -fn increment_id() -> u64 { +/*fn increment_id() -> u64 { let atomic = unsafe { &CTX.id }; atomic.fetch_add(1, Ordering::SeqCst) -} +}*/ /// Initialize the front-end library #[wasm_bindgen] pub fn init_usdpl(port: u16) { + #[cfg(feature = "debug")] + web_sys::console::log_1(&format!("init_usdpl(port={})", port).into()); #[cfg(feature = "console_error_panic_hook")] console_error_panic_hook::set_once(); #[cfg(feature = "console_log")] console_log::init_with_level(log::Level::Debug).expect("USDPL: error initializing console log"); - //REMOTE_PORT.store(port, std::sync::atomic::Ordering::SeqCst); - unsafe { + + /*unsafe { CTX = UsdplContext { port: port, - id: AtomicU64::new(0), + //id: AtomicU64::new(0), #[cfg(feature = "encrypt")] key: encryption_key(), }; - } + }*/ unsafe { CACHE = Some(std::collections::HashMap::new()); } + + #[cfg(feature = "debug")] + web_sys::console::log_1(&format!("USDPL:{} init succeeded", port).into()); + log::info!("USDPL:{} init succeeded", port); } /// Get the targeted plugin framework, or "any" if unknown @@ -134,16 +140,17 @@ pub fn get_value(key: String) -> JsValue { } } +/* /// Call a function on the back-end. /// Returns null (None) if this fails for any reason. #[wasm_bindgen] pub async fn call_backend(name: String, parameters: Vec) -> JsValue { #[cfg(feature = "debug")] - imports::console_log(&format!( + web_sys::console::log_1(&format!( "call_backend({}, [params; {}])", name, parameters.len() - )); + ).into()); let next_id = increment_id(); let mut params = Vec::with_capacity(parameters.len()); for val in parameters { @@ -151,7 +158,7 @@ pub async fn call_backend(name: String, parameters: Vec) -> JsValue { } let port = get_port(); #[cfg(feature = "debug")] - imports::console_log(&format!("USDPL: Got port {}", port)); + web_sys::console::log_1(&format!("USDPL: Got port {}", port).into()); let results = connection::send_call( next_id, Packet::Call(RemoteCall { @@ -169,7 +176,7 @@ pub async fn call_backend(name: String, parameters: Vec) -> JsValue { #[allow(unused_variables)] Err(e) => { #[cfg(feature = "debug")] - imports::console_error(&format!("USDPL: Got error while calling {}: {:?}", name, e)); + web_sys::console::error_1(&format!("USDPL: Got error while calling {}: {:?}", name, e).into()); return JsValue::NULL; } }; @@ -197,7 +204,7 @@ pub async fn init_tr(locale: String) { { Ok(Packet::Translations(translations)) => { #[cfg(feature = "debug")] - imports::console_log(&format!("USDPL: Got translations for {}", locale)); + web_sys::console::log_1(&format!("USDPL: Got translations for {}", locale).into()); // convert translations into map let mut tr_map = std::collections::HashMap::with_capacity(translations.len()); for (key, val) in translations { @@ -207,17 +214,17 @@ pub async fn init_tr(locale: String) { } Ok(_) => { #[cfg(feature = "debug")] - imports::console_error(&format!("USDPL: Got wrong packet response for init_tr")); + web_sys::console::error_1(&format!("USDPL: Got wrong packet response for init_tr").into()); unsafe { TRANSLATIONS = None } } #[allow(unused_variables)] Err(e) => { #[cfg(feature = "debug")] - imports::console_error(&format!("USDPL: Got wrong error for init_tr: {:#?}", e)); + web_sys::console::error_1(&format!("USDPL: Got wrong error for init_tr: {:#?}", e).into()); unsafe { TRANSLATIONS = None } } } -} +}*/ /// Translate a phrase, equivalent to tr_n(msg_id, 0) #[wasm_bindgen] From 44298f660f7765d288a9f272fdd27766898d4e29 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sun, 3 Sep 2023 17:33:30 -0400 Subject: [PATCH 10/12] Remove old APIs, improve/fix front logging --- Cargo.lock | 12 -- usdpl-back/Cargo.toml | 6 +- usdpl-back/src/api_common/files.rs | 44 ----- usdpl-back/src/api_common/mod.rs | 1 - usdpl-build/Cargo.toml | 5 + usdpl-build/src/front/service_generator.rs | 3 + usdpl-core/Cargo.toml | 5 +- usdpl-core/src/lib.rs | 7 - usdpl-core/src/remote_call.rs | 114 ------------- usdpl-core/src/serdes/dump_impl.rs | 157 ----------------- usdpl-core/src/serdes/load_impl.rs | 185 --------------------- usdpl-core/src/serdes/mod.rs | 10 -- usdpl-core/src/serdes/primitive.rs | 163 ------------------ usdpl-core/src/serdes/traits.rs | 150 ----------------- usdpl-core/src/socket.rs | 174 ------------------- usdpl-front/Cargo.toml | 9 +- usdpl-front/src/client_handler.rs | 63 ++----- usdpl-front/src/connection.rs | 126 -------------- usdpl-front/src/console_logs.rs | 52 ++++++ usdpl-front/src/lib.rs | 182 ++++---------------- 20 files changed, 115 insertions(+), 1353 deletions(-) delete mode 100644 usdpl-back/src/api_common/files.rs delete mode 100644 usdpl-core/src/remote_call.rs delete mode 100644 usdpl-core/src/serdes/dump_impl.rs delete mode 100644 usdpl-core/src/serdes/load_impl.rs delete mode 100644 usdpl-core/src/serdes/mod.rs delete mode 100644 usdpl-core/src/serdes/primitive.rs delete mode 100644 usdpl-core/src/serdes/traits.rs delete mode 100644 usdpl-core/src/socket.rs delete mode 100644 usdpl-front/src/connection.rs create mode 100644 usdpl-front/src/console_logs.rs diff --git a/Cargo.lock b/Cargo.lock index b70aa29..74c7017 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,17 +196,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "console_log" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f" -dependencies = [ - "log", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "convert_case" version = "0.4.0" @@ -1565,7 +1554,6 @@ name = "usdpl-front" version = "0.11.0" dependencies = [ "console_error_panic_hook", - "console_log", "futures", "futures-channel", "gloo-net", diff --git a/usdpl-back/Cargo.toml b/usdpl-back/Cargo.toml index 872c61d..67e03cd 100644 --- a/usdpl-back/Cargo.toml +++ b/usdpl-back/Cargo.toml @@ -2,8 +2,9 @@ name = "usdpl-back" version = "0.11.0" edition = "2021" +authors = ["NGnius "] license = "GPL-3.0-only" -repository = "https://github.com/NGnius/usdpl-rs" +repository = "https://git.ngni.us/NG-SD-Plugins/usdpl-rs" readme = "../README.md" description = "Universal Steam Deck Plugin Library back-end" @@ -11,7 +12,7 @@ description = "Universal Steam Deck Plugin Library back-end" default = ["blocking"] decky = ["usdpl-core/decky"] blocking = [] # synchronous API for async functionality, using tokio -encrypt = ["usdpl-core/encrypt", "obfstr", "hex"] +#encrypt = ["usdpl-core", "obfstr", "hex"] [dependencies] usdpl-core = { version = "0.11", path = "../usdpl-core"} @@ -27,7 +28,6 @@ prost = "0.11" ratchet_rs = { version = "0.4", features = [ "deflate" ] } # HTTP web framework -#warp = { version = "0.3" } bytes = { version = "1.1" } tokio = { version = "1", features = [ "full" ]} diff --git a/usdpl-back/src/api_common/files.rs b/usdpl-back/src/api_common/files.rs deleted file mode 100644 index f3085a3..0000000 --- a/usdpl-back/src/api_common/files.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! Common low-level file operations -use std::fmt::Display; -use std::fs::File; -use std::io::{self, Read, Write}; -use std::path::Path; -use std::str::FromStr; - -/// Write something to a file. -/// Useful for kernel configuration files. -#[inline] -pub fn write_single, D: Display>(path: P, display: D) -> Result<(), io::Error> { - let mut file = File::create(path)?; - write!(file, "{}", display) -} - -/// read_single error -#[derive(Debug)] -pub enum ReadError { - /// IO Error - Io(io::Error), - /// String parsing error - Parse(E), -} - -impl std::fmt::Display for ReadError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Io(io) => write!(f, "io: {}", io), - Self::Parse(e) => write!(f, "parse: {}", e), - } - } -} - -impl std::error::Error for ReadError {} - -/// Read something from a file. -/// Useful for kernel configuration files. -#[inline] -pub fn read_single, D: FromStr, E>(path: P) -> Result> { - let mut file = File::open(path).map_err(ReadError::Io)?; - let mut string = String::new(); - file.read_to_string(&mut string).map_err(ReadError::Io)?; - string.trim().parse().map_err(ReadError::Parse) -} diff --git a/usdpl-back/src/api_common/mod.rs b/usdpl-back/src/api_common/mod.rs index 63763e5..16ec367 100644 --- a/usdpl-back/src/api_common/mod.rs +++ b/usdpl-back/src/api_common/mod.rs @@ -1,2 +1 @@ pub mod dirs; -pub mod files; diff --git a/usdpl-build/Cargo.toml b/usdpl-build/Cargo.toml index 796f836..9e0a40a 100644 --- a/usdpl-build/Cargo.toml +++ b/usdpl-build/Cargo.toml @@ -2,6 +2,11 @@ name = "usdpl-build" version = "0.11.0" edition = "2021" +authors = ["NGnius "] +license = "GPL-3.0-only" +repository = "https://git.ngni.us/NG-SD-Plugins/usdpl-rs" +readme = "../README.md" +description = "Universal Steam Deck Plugin Library core" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/usdpl-build/src/front/service_generator.rs b/usdpl-build/src/front/service_generator.rs index 5391926..d3dd5f9 100644 --- a/usdpl-build/src/front/service_generator.rs +++ b/usdpl-build/src/front/service_generator.rs @@ -1026,6 +1026,7 @@ impl IServiceGenerator for WasmServiceGenerator { .expect("FileDescriptorSet required for WASM service generator"); let service_struct_name = quote::format_ident!("{}Client", service.name); let service_js_name = quote::format_ident!("{}", service.name); + let service_str_name = service.name.clone(); let service_methods = generate_service_methods(&service, fds); let service_types = generate_service_io_types(&service, fds); let mod_name = quote::format_ident!("js_{}", service.name.to_lowercase()); @@ -1059,9 +1060,11 @@ impl IServiceGenerator for WasmServiceGenerator { impl #service_js_name { #[wasm_bindgen(constructor)] pub fn new(port: u16) -> Self { + usdpl_front::init_usdpl(); let implementation = super::#service_struct_name::new( WebSocketHandler::new(port) ); + log::info!("Initialized ws service {} on port {}", #service_str_name, port); Self { service: implementation, } diff --git a/usdpl-core/Cargo.toml b/usdpl-core/Cargo.toml index b196650..e9c1e5e 100644 --- a/usdpl-core/Cargo.toml +++ b/usdpl-core/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "usdpl-core" version = "0.11.0" +authors = ["NGnius "] edition = "2021" license = "GPL-3.0-only" -repository = "https://github.com/NGnius/usdpl-rs" +repository = "https://git.ngni.us/NG-SD-Plugins/usdpl-rs" readme = "../README.md" -description = "Universal Steam Deck Plugin Library core" +description = "Universal Steam Deck Plugin Library core designed for all architectures" [features] default = [] diff --git a/usdpl-core/src/lib.rs b/usdpl-core/src/lib.rs index f5453bc..18ddfb9 100644 --- a/usdpl-core/src/lib.rs +++ b/usdpl-core/src/lib.rs @@ -2,19 +2,12 @@ //! This contains serialization functionality and networking datatypes. #![warn(missing_docs)] -mod remote_call; - #[cfg(not(any(feature = "decky")))] mod api_any; mod api_common; #[cfg(all(feature = "decky", not(any(feature = "any"))))] mod api_decky; -pub mod serdes; -pub mod socket; - -pub use remote_call::{RemoteCall, RemoteCallResponse}; - /// USDPL core API. /// This contains functionality used in both the back-end and front-end. pub mod api { diff --git a/usdpl-core/src/remote_call.rs b/usdpl-core/src/remote_call.rs deleted file mode 100644 index 973f0db..0000000 --- a/usdpl-core/src/remote_call.rs +++ /dev/null @@ -1,114 +0,0 @@ -use std::io::{Read, Write}; - -use crate::serdes::{DumpError, Dumpable, LoadError, Loadable, Primitive}; - -/// Remote call packet representing a function to call on the back-end, sent from the front-end -pub struct RemoteCall { - /// The call id assigned by the front-end - pub id: u64, - /// The function's name - pub function: String, - /// The function's input parameters - pub parameters: Vec, -} - -impl Loadable for RemoteCall { - fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let (id_num, len0) = u64::load(buffer)?; - let (function_name, len1) = String::load(buffer)?; - let (params, len2) = Vec::::load(buffer)?; - Ok(( - Self { - id: id_num, - function: function_name, - parameters: params, - }, - len0 + len1 + len2, - )) - } -} - -impl Dumpable for RemoteCall { - fn dump(&self, buffer: &mut dyn Write) -> Result { - let len0 = self.id.dump(buffer)?; - let len1 = self.function.dump(buffer)?; - let len2 = self.parameters.dump(buffer)?; - Ok(len0 + len1 + len2) - } -} - -/// Remote call response packet representing the response from a remote call after the back-end has executed it. -pub struct RemoteCallResponse { - /// The call id from the RemoteCall - pub id: u64, - /// The function's result - pub response: Vec, -} - -impl Loadable for RemoteCallResponse { - fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let (id_num, len0) = u64::load(buffer)?; - let (response_var, len1) = Vec::::load(buffer)?; - Ok(( - Self { - id: id_num, - response: response_var, - }, - len0 + len1, - )) - } -} - -impl Dumpable for RemoteCallResponse { - fn dump(&self, buffer: &mut dyn Write) -> Result { - let len0 = self.id.dump(buffer)?; - let len1 = self.response.dump(buffer)?; - Ok(len0 + len1) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn remote_call_idempotence_test() { - let call = RemoteCall { - id: 42, - function: "something very long just in case this causes unexpected issues".into(), - parameters: vec!["param1".into(), 42f64.into()], - }; - - let mut buffer = String::with_capacity(crate::socket::PACKET_BUFFER_SIZE); - let len = call.dump_base64(&mut buffer).unwrap(); - - println!("base64 dumped: `{}` (len: {})", buffer, len); - - let (loaded_call, loaded_len) = RemoteCall::load_base64(buffer.as_bytes()).unwrap(); - assert_eq!(len, loaded_len, "Expected load and dump lengths to match"); - - assert_eq!(loaded_call.id, call.id, "RemoteCall.id does not match"); - assert_eq!( - loaded_call.function, call.function, - "RemoteCall.function does not match" - ); - if let Primitive::String(loaded) = &loaded_call.parameters[0] { - if let Primitive::String(original) = &call.parameters[0] { - assert_eq!(loaded, original, "RemoteCall.parameters[0] does not match"); - } else { - panic!("Original call parameter 0 is not String") - } - } else { - panic!("Loaded call parameter 0 is not String") - } - if let Primitive::F64(loaded) = &loaded_call.parameters[1] { - if let Primitive::F64(original) = &call.parameters[1] { - assert_eq!(loaded, original, "RemoteCall.parameters[1] does not match"); - } else { - panic!("Original call parameter 1 is not f64") - } - } else { - panic!("Loaded call parameter 1 is not f64") - } - } -} diff --git a/usdpl-core/src/serdes/dump_impl.rs b/usdpl-core/src/serdes/dump_impl.rs deleted file mode 100644 index 61551fe..0000000 --- a/usdpl-core/src/serdes/dump_impl.rs +++ /dev/null @@ -1,157 +0,0 @@ -use std::io::Write; - -use super::{DumpError, Dumpable}; - -impl Dumpable for String { - fn dump(&self, buffer: &mut dyn Write) -> Result { - let str_bytes = self.as_bytes(); - let len_bytes = (str_bytes.len() as u32).to_le_bytes(); - let size1 = buffer.write(&len_bytes).map_err(DumpError::Io)?; - let size2 = buffer.write(&str_bytes).map_err(DumpError::Io)?; - Ok(size1 + size2) - } -} - -impl Dumpable for Vec { - fn dump(&self, buffer: &mut dyn Write) -> Result { - let len_bytes = (self.len() as u32).to_le_bytes(); - let mut total = buffer.write(&len_bytes).map_err(DumpError::Io)?; - for obj in self.iter() { - let len = obj.dump(buffer)?; - total += len; - } - Ok(total) - } -} - -impl Dumpable for (T0, T1) { - fn dump(&self, buffer: &mut dyn Write) -> Result { - Ok(self.0.dump(buffer)? + self.1.dump(buffer)?) - } -} - -impl Dumpable for (T0, T1, T2) { - fn dump(&self, buffer: &mut dyn Write) -> Result { - Ok(self.0.dump(buffer)? + self.1.dump(buffer)? + self.2.dump(buffer)?) - } -} - -impl Dumpable for (T0, T1, T2, T3) { - fn dump(&self, buffer: &mut dyn Write) -> Result { - Ok(self.0.dump(buffer)? - + self.1.dump(buffer)? - + self.2.dump(buffer)? - + self.3.dump(buffer)?) - } -} - -impl Dumpable - for (T0, T1, T2, T3, T4) -{ - fn dump(&self, buffer: &mut dyn Write) -> Result { - Ok(self.0.dump(buffer)? - + self.1.dump(buffer)? - + self.2.dump(buffer)? - + self.3.dump(buffer)? - + self.4.dump(buffer)?) - } -} - -impl Dumpable for bool { - fn dump(&self, buffer: &mut dyn Write) -> Result { - buffer.write(&[*self as u8]).map_err(DumpError::Io) - } -} - -impl Dumpable for u8 { - fn dump(&self, buffer: &mut dyn Write) -> Result { - buffer.write(&[*self]).map_err(DumpError::Io) - } -} - -/*impl Dumpable for i8 { - fn dump(&self, buffer: &mut dyn Write) -> Result { - buffer.write(&self.to_le_bytes()).map_err(DumpError::Io) - } -}*/ - -macro_rules! int_impl { - ($type:ty) => { - impl Dumpable for $type { - fn dump(&self, buffer: &mut dyn Write) -> Result { - buffer.write(&self.to_le_bytes()).map_err(DumpError::Io) - } - } - }; -} - -int_impl! {u16} -int_impl! {u32} -int_impl! {u64} -int_impl! {u128} - -int_impl! {i8} -int_impl! {i16} -int_impl! {i32} -int_impl! {i64} -int_impl! {i128} - -int_impl! {f32} -int_impl! {f64} - -#[cfg(test)] -mod tests { - use super::*; - - macro_rules! test_impl { - ($fn_name:ident, $data:expr, $expected_len:literal, $expected_dump:expr) => { - #[test] - fn $fn_name() { - let data = $data; - let mut buffer = Vec::with_capacity(128); - let write_len = data.dump(&mut buffer).expect("Dump not ok"); - assert_eq!(write_len, $expected_len, "Wrong amount written"); - assert_eq!(&buffer[..write_len], $expected_dump); - println!("Dumped {:?}", buffer.as_slice()); - } - }; - } - - test_impl! {string_dump_test, "test".to_string(), 8, &[4, 0, 0, 0, 116, 101, 115, 116]} - - test_impl! { - vec_dump_test, - vec![ - "".to_string(), - "test1".to_string(), - "test2".to_string() - ], - 26, - &[3, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 5, 0, 0, 0, 116, 101, 115, 116, 50] - } - - test_impl! {tuple2_dump_test, (0u8, 1u8), 2, &[0, 1]} - test_impl! {tuple3_dump_test, (0u8, 1u8, 2u8), 3, &[0, 1, 2]} - test_impl! {tuple4_dump_test, (0u8, 1u8, 2u8, 3u8), 4, &[0, 1, 2, 3]} - test_impl! {tuple5_dump_test, (0u8, 1u8, 2u8, 3u8, 4u8), 5, &[0, 1, 2, 3, 4]} - - test_impl! {bool_true_dump_test, true, 1, &[1]} - test_impl! {bool_false_dump_test, false, 1, &[0]} - - // testing macro-generated code isn't particularly useful, but do it anyway - - test_impl! {u8_dump_test, 42u8, 1, &[42]} - test_impl! {u16_dump_test, 42u16, 2, &[42, 0]} - test_impl! {u32_dump_test, 42u32, 4, &[42, 0, 0, 0]} - test_impl! {u64_dump_test, 42u64, 8, &[42, 0, 0, 0, 0, 0, 0, 0]} - test_impl! {u128_dump_test, 42u128, 16, &[42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]} - - test_impl! {i8_dump_test, 42i8, 1, &[42]} - test_impl! {i16_dump_test, 42i16, 2, &[42, 0]} - test_impl! {i32_dump_test, 42i32, 4, &[42, 0, 0, 0]} - test_impl! {i64_dump_test, 42i64, 8, &[42, 0, 0, 0, 0, 0, 0, 0]} - test_impl! {i128_dump_test, 42i128, 16, &[42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]} - - test_impl! {f32_dump_test, 42f32, 4, &[0, 0, 40, 66]} - test_impl! {f64_dump_test, 42f64, 8, &[0, 0, 0, 0, 0, 0, 69, 64]} -} diff --git a/usdpl-core/src/serdes/load_impl.rs b/usdpl-core/src/serdes/load_impl.rs deleted file mode 100644 index f2aa9bf..0000000 --- a/usdpl-core/src/serdes/load_impl.rs +++ /dev/null @@ -1,185 +0,0 @@ -use std::io::Read; - -use super::{LoadError, Loadable}; - -impl Loadable for String { - fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let mut u32_bytes: [u8; 4] = [u8::MAX; 4]; - buffer.read_exact(&mut u32_bytes).map_err(LoadError::Io)?; - let str_size = u32::from_le_bytes(u32_bytes) as usize; - //let mut str_buf = String::with_capacity(str_size); - let mut str_buf = Vec::with_capacity(str_size); - let mut byte_buf = [u8::MAX; 1]; - for _ in 0..str_size { - buffer.read_exact(&mut byte_buf).map_err(LoadError::Io)?; - str_buf.push(byte_buf[0]); - } - //let size2 = buffer.read_to_string(&mut str_buf).map_err(LoadError::Io)?; - Ok(( - String::from_utf8(str_buf).map_err(|_| LoadError::InvalidData)?, - str_size + 4, - )) - } -} - -impl Loadable for Vec { - fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let mut u32_bytes: [u8; 4] = [u8::MAX; 4]; - buffer.read_exact(&mut u32_bytes).map_err(LoadError::Io)?; - let count = u32::from_le_bytes(u32_bytes) as usize; - let mut cursor = 4; - let mut items = Vec::with_capacity(count); - for _ in 0..count { - let (obj, len) = T::load(buffer)?; - cursor += len; - items.push(obj); - } - Ok((items, cursor)) - } -} - -impl Loadable for (T0, T1) { - fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let (t0, len0) = T0::load(buffer)?; - let (t1, len1) = T1::load(buffer)?; - Ok(((t0, t1), len0 + len1)) - } -} - -impl Loadable for (T0, T1, T2) { - fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let (t0, len0) = T0::load(buffer)?; - let (t1, len1) = T1::load(buffer)?; - let (t2, len2) = T2::load(buffer)?; - Ok(((t0, t1, t2), len0 + len1 + len2)) - } -} - -impl Loadable for (T0, T1, T2, T3) { - fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let (t0, len0) = T0::load(buffer)?; - let (t1, len1) = T1::load(buffer)?; - let (t2, len2) = T2::load(buffer)?; - let (t3, len3) = T3::load(buffer)?; - Ok(((t0, t1, t2, t3), len0 + len1 + len2 + len3)) - } -} - -impl Loadable - for (T0, T1, T2, T3, T4) -{ - fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let (t0, len0) = T0::load(buffer)?; - let (t1, len1) = T1::load(buffer)?; - let (t2, len2) = T2::load(buffer)?; - let (t3, len3) = T3::load(buffer)?; - let (t4, len4) = T4::load(buffer)?; - Ok(((t0, t1, t2, t3, t4), len0 + len1 + len2 + len3 + len4)) - } -} - -impl Loadable for bool { - fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let mut byte = [u8::MAX; 1]; - buffer.read_exact(&mut byte).map_err(LoadError::Io)?; - Ok((byte[0] != 0, 1)) - } -} - -impl Loadable for u8 { - fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let mut byte = [u8::MAX; 1]; - buffer.read_exact(&mut byte).map_err(LoadError::Io)?; - Ok((byte[0], 1)) - } -} - -impl Loadable for i8 { - fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let mut byte = [u8::MAX; 1]; - buffer.read_exact(&mut byte).map_err(LoadError::Io)?; - Ok((i8::from_le_bytes(byte), 1)) - } -} - -macro_rules! int_impl { - ($type:ty, $size:literal) => { - impl Loadable for $type { - fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let mut bytes: [u8; $size] = [u8::MAX; $size]; - buffer.read_exact(&mut bytes).map_err(LoadError::Io)?; - let i = <$type>::from_le_bytes(bytes); - Ok((i, $size)) - } - } - }; -} - -int_impl! {u16, 2} -int_impl! {u32, 4} -int_impl! {u64, 8} -int_impl! {u128, 16} - -int_impl! {i16, 2} -int_impl! {i32, 4} -int_impl! {i64, 8} -int_impl! {i128, 16} - -int_impl! {f32, 4} -int_impl! {f64, 8} - -#[cfg(test)] -mod tests { - use super::*; - use std::io::Cursor; - - macro_rules! test_impl { - ($fn_name:ident, $data:expr, $type:ty, $expected_len:literal, $expected_load:expr) => { - #[test] - fn $fn_name() { - let buffer_data = $data; - let mut buffer = Vec::with_capacity(buffer_data.len()); - buffer.extend_from_slice(&buffer_data); - let (obj, read_len) = <$type>::load(&mut Cursor::new(buffer)).expect("Load not ok"); - assert_eq!(read_len, $expected_len, "Wrong amount read"); - assert_eq!(obj, $expected_load, "Loaded value not as expected"); - println!("Loaded {:?}", obj); - } - }; - } - - test_impl! {string_load_test, [4u8, 0, 0, 0, 116, 101, 115, 116, 0, 128], String, 8, "test"} - test_impl! { - vec_load_test, - [3u8, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 5, 0, 0, 0, 116, 101, 115, 116, 50], - Vec, - 26, - vec![ - "".to_string(), - "test1".to_string(), - "test2".to_string() - ] - } - - test_impl! {tuple2_load_test, [0, 1], (u8, u8), 2, (0, 1)} - - test_impl! {bool_true_load_test, [1], bool, 1, true} - test_impl! {bool_false_load_test, [0], bool, 1, false} - - // testing macro-generated code isn't particularly useful, but do it anyway - - test_impl! {u8_load_test, [42], u8, 1, 42u8} - test_impl! {u16_load_test, [42, 0], u16, 2, 42u16} - test_impl! {u32_load_test, [42, 0, 0, 0], u32, 4, 42u32} - test_impl! {u64_load_test, [42, 0, 0, 0, 0, 0, 0, 0], u64, 8, 42u64} - test_impl! {u128_load_test, [42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], u128, 16, 42u128} - - test_impl! {i8_load_test, [42], i8, 1, 42i8} - test_impl! {i16_load_test, [42, 0], i16, 2, 42i16} - test_impl! {i32_load_test, [42, 0, 0, 0], i32, 4, 42i32} - test_impl! {i64_load_test, [42, 0, 0, 0, 0, 0, 0, 0], i64, 8, 42i64} - test_impl! {i128_load_test, [42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], i128, 16, 42i128} - - test_impl! {f32_load_test, [0, 0, 40, 66], f32, 4, 42f32} - test_impl! {f64_load_test, [0, 0, 0, 0, 0, 0, 69, 64], f64, 8, 42f64} -} diff --git a/usdpl-core/src/serdes/mod.rs b/usdpl-core/src/serdes/mod.rs deleted file mode 100644 index f911e6d..0000000 --- a/usdpl-core/src/serdes/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! Serialization and deserialization functionality. -//! Little endian is preferred. - -mod dump_impl; -mod load_impl; -mod primitive; -mod traits; - -pub use primitive::Primitive; -pub use traits::{DumpError, Dumpable, LoadError, Loadable}; diff --git a/usdpl-core/src/serdes/primitive.rs b/usdpl-core/src/serdes/primitive.rs deleted file mode 100644 index 9793bff..0000000 --- a/usdpl-core/src/serdes/primitive.rs +++ /dev/null @@ -1,163 +0,0 @@ -use super::{DumpError, Dumpable, LoadError, Loadable}; -use std::io::{Read, Write}; - -/// Primitive types supported for communication between the USDPL back- and front-end. -/// These are used for sending over the TCP connection. -pub enum Primitive { - /// Null or unsupported object - Empty, - /// String-like - String(String), - /// f32 - F32(f32), - /// f64 - F64(f64), - /// u32 - U32(u32), - /// u64 - U64(u64), - /// i32 - I32(i32), - /// i64 - I64(i64), - /// boolean - Bool(bool), - /// Non-primitive in Json format - Json(String), -} - -impl Primitive { - /// Discriminant -- first byte of a dumped primitive - const fn discriminant(&self) -> u8 { - match self { - Self::Empty => 1, - Self::String(_) => 2, - Self::F32(_) => 3, - Self::F64(_) => 4, - Self::U32(_) => 5, - Self::U64(_) => 6, - Self::I32(_) => 7, - Self::I64(_) => 8, - Self::Bool(_) => 9, - Self::Json(_) => 10, - } - } -} - -impl Loadable for Primitive { - fn load(buf: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let mut discriminant_buf = [u8::MAX; 1]; - buf.read_exact(&mut discriminant_buf) - .map_err(LoadError::Io)?; - let mut result: (Self, usize) = match discriminant_buf[0] { - //0 => (None, 0), - 1 => (Self::Empty, 0), - 2 => String::load(buf).map(|(obj, len)| (Self::String(obj), len))?, - 3 => f32::load(buf).map(|(obj, len)| (Self::F32(obj), len))?, - 4 => f64::load(buf).map(|(obj, len)| (Self::F64(obj), len))?, - 5 => u32::load(buf).map(|(obj, len)| (Self::U32(obj), len))?, - 6 => u64::load(buf).map(|(obj, len)| (Self::U64(obj), len))?, - 7 => i32::load(buf).map(|(obj, len)| (Self::I32(obj), len))?, - 8 => i64::load(buf).map(|(obj, len)| (Self::I64(obj), len))?, - 9 => bool::load(buf).map(|(obj, len)| (Self::Bool(obj), len))?, - 10 => String::load(buf).map(|(obj, len)| (Self::Json(obj), len))?, - _ => return Err(LoadError::InvalidData), - }; - result.1 += 1; - Ok(result) - } -} - -impl Dumpable for Primitive { - fn dump(&self, buf: &mut dyn Write) -> Result { - let size1 = buf.write(&[self.discriminant()]).map_err(DumpError::Io)?; - let result = match self { - Self::Empty => Ok(0), - Self::String(s) => s.dump(buf), - Self::F32(x) => x.dump(buf), - Self::F64(x) => x.dump(buf), - Self::U32(x) => x.dump(buf), - Self::U64(x) => x.dump(buf), - Self::I32(x) => x.dump(buf), - Self::I64(x) => x.dump(buf), - Self::Bool(x) => x.dump(buf), - Self::Json(x) => x.dump(buf), - }?; - Ok(size1 + result) - } -} - -impl std::convert::Into for &str { - fn into(self) -> Primitive { - Primitive::String(self.to_string()) - } -} - -impl std::convert::Into for () { - fn into(self) -> Primitive { - Primitive::Empty - } -} - -macro_rules! into_impl { - ($type:ty, $variant:ident) => { - impl std::convert::Into for $type { - fn into(self) -> Primitive { - Primitive::$variant(self) - } - } - }; -} - -into_impl! {String, String} -into_impl! {bool, Bool} - -into_impl! {u32, U32} -into_impl! {u64, U64} - -into_impl! {i32, I32} -into_impl! {i64, I64} - -into_impl! {f32, F32} -into_impl! {f64, F64} - -#[cfg(test)] -mod tests { - use super::*; - use std::io::Cursor; - - #[test] - fn string_idempotence_test() { - let data = "Test"; - let primitive = Primitive::String(data.to_string()); - let mut buffer = Vec::with_capacity(128); - let write_len = primitive.dump(&mut buffer).expect("Dump not ok"); - let (obj, read_len) = Primitive::load(&mut Cursor::new(buffer)).expect("Load not ok"); - assert_eq!( - write_len, read_len, - "Amount written and amount read do not match" - ); - if let Primitive::String(result) = obj { - assert_eq!(data, result, "Data written and read does not match"); - } else { - panic!("Read non-string primitive"); - } - } - - #[test] - fn empty_idempotence_test() { - let primitive = Primitive::Empty; - let mut buffer = Vec::with_capacity(128); - let write_len = primitive.dump(&mut buffer).expect("Dump not ok"); - let (obj, read_len) = Primitive::load(&mut Cursor::new(buffer)).expect("Load not ok"); - assert_eq!( - write_len, read_len, - "Amount written and amount read do not match" - ); - if let Primitive::Empty = obj { - //assert_eq!(data, result, "Data written and read does not match"); - } else { - panic!("Read non-string primitive"); - } - } -} diff --git a/usdpl-core/src/serdes/traits.rs b/usdpl-core/src/serdes/traits.rs deleted file mode 100644 index b11b5db..0000000 --- a/usdpl-core/src/serdes/traits.rs +++ /dev/null @@ -1,150 +0,0 @@ -use base64::{decode_config_buf, encode_config_buf, Config}; -use std::io::{Cursor, Read, Write}; - -const B64_CONF: Config = Config::new(base64::CharacterSet::Standard, true); - -#[cfg(feature = "encrypt")] -const ASSOCIATED_DATA: &[u8] = b"usdpl-core-data"; - -#[cfg(feature = "encrypt")] -use aes_gcm_siv::aead::{AeadInPlace, NewAead}; - -/// Errors from Loadable::load -#[derive(Debug)] -pub enum LoadError { - /// Buffer smaller than expected - TooSmallBuffer, - /// Unexpected/corrupted data encountered - InvalidData, - /// Encrypted data cannot be decrypted - #[cfg(feature = "encrypt")] - DecryptionError, - /// Read error - Io(std::io::Error), - /// Unimplemented - #[cfg(debug_assertions)] - Todo, -} - -impl std::fmt::Display for LoadError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Self::TooSmallBuffer => write!(f, "LoadError: TooSmallBuffer"), - Self::InvalidData => write!(f, "LoadError: InvalidData"), - #[cfg(feature = "encrypt")] - Self::DecryptionError => write!(f, "LoadError: DecryptionError"), - Self::Io(err) => write!(f, "LoadError: Io({})", err), - #[cfg(debug_assertions)] - Self::Todo => write!(f, "LoadError: TODO!"), - } - } -} - -/// Load an object from the buffer -pub trait Loadable: Sized { - /// Read the buffer, building the object and returning the amount of bytes read. - /// If anything is wrong with the buffer, Err should be returned. - fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError>; - - /// Load data from a base64-encoded buffer - fn load_base64(buffer: &[u8]) -> Result<(Self, usize), LoadError> { - let mut buffer2 = Vec::with_capacity(crate::socket::PACKET_BUFFER_SIZE); - decode_config_buf(buffer, B64_CONF, &mut buffer2).map_err(|_| LoadError::InvalidData)?; - let mut cursor = Cursor::new(buffer2); - Self::load(&mut cursor) - } - - /// Load data from an encrypted base64-encoded buffer - #[cfg(feature = "encrypt")] - fn load_encrypted(buffer: &[u8], key: &[u8], nonce: &[u8]) -> Result<(Self, usize), LoadError> { - //println!("encrypted buffer: {}", String::from_utf8(buffer.to_vec()).unwrap()); - let key = aes_gcm_siv::Key::from_slice(key); - let cipher = aes_gcm_siv::Aes256GcmSiv::new(key); - let nonce = aes_gcm_siv::Nonce::from_slice(nonce); - let mut decoded_buf = Vec::with_capacity(crate::socket::PACKET_BUFFER_SIZE); - base64::decode_config_buf(buffer, B64_CONF, &mut decoded_buf) - .map_err(|_| LoadError::InvalidData)?; - //println!("Decoded buf: {:?}", decoded_buf); - cipher - .decrypt_in_place(nonce, ASSOCIATED_DATA, &mut decoded_buf) - .map_err(|_| LoadError::DecryptionError)?; - //println!("Decrypted buf: {:?}", decoded_buf); - let mut cursor = Cursor::new(decoded_buf); - Self::load(&mut cursor) - } -} - -/// Errors from Dumpable::dump -#[derive(Debug)] -pub enum DumpError { - /// Buffer not big enough to dump data into - TooSmallBuffer, - /// Data cannot be dumped - Unsupported, - /// Data cannot be encrypted - #[cfg(feature = "encrypt")] - EncryptionError, - /// Write error - Io(std::io::Error), - /// Unimplemented - #[cfg(debug_assertions)] - Todo, -} - -impl std::fmt::Display for DumpError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Self::TooSmallBuffer => write!(f, "DumpError: TooSmallBuffer"), - Self::Unsupported => write!(f, "DumpError: Unsupported"), - #[cfg(feature = "encrypt")] - Self::EncryptionError => write!(f, "DumpError: EncryptionError"), - Self::Io(err) => write!(f, "DumpError: Io({})", err), - #[cfg(debug_assertions)] - Self::Todo => write!(f, "DumpError: TODO!"), - } - } -} - -/// Dump an object into the buffer -pub trait Dumpable { - /// Write the object to the buffer, returning the amount of bytes written. - /// If anything is wrong, false should be returned. - fn dump(&self, buffer: &mut dyn Write) -> Result; - - /// Dump data as base64-encoded. - /// Useful for transmitting data as text. - fn dump_base64(&self, buffer: &mut String) -> Result { - let mut buffer2 = Vec::with_capacity(crate::socket::PACKET_BUFFER_SIZE); - let len = self.dump(&mut buffer2)?; - encode_config_buf(&buffer2[..len], B64_CONF, buffer); - Ok(len) - } - - /// Dump data as an encrypted base64-encoded buffer - #[cfg(feature = "encrypt")] - fn dump_encrypted( - &self, - buffer: &mut Vec, - key: &[u8], - nonce: &[u8], - ) -> Result { - let mut buffer2 = Vec::with_capacity(crate::socket::PACKET_BUFFER_SIZE); - let size = self.dump(&mut buffer2)?; - buffer2.truncate(size); - //println!("Buf: {:?}", buffer2); - let key = aes_gcm_siv::Key::from_slice(key); - let cipher = aes_gcm_siv::Aes256GcmSiv::new(key); - let nonce = aes_gcm_siv::Nonce::from_slice(nonce); - cipher - .encrypt_in_place(nonce, ASSOCIATED_DATA, &mut buffer2) - .map_err(|_| DumpError::EncryptionError)?; - //println!("Encrypted slice: {:?}", &buffer2); - let mut base64_buf = String::with_capacity(crate::socket::PACKET_BUFFER_SIZE); - encode_config_buf(buffer2.as_slice(), B64_CONF, &mut base64_buf); - //println!("base64 len: {}", base64_buf.as_bytes().len()); - buffer.extend_from_slice(base64_buf.as_bytes()); - //let string = String::from_utf8(buffer.as_slice().to_vec()).unwrap(); - //println!("Encoded slice: {}", string); - Ok(base64_buf.len()) - } -} diff --git a/usdpl-core/src/socket.rs b/usdpl-core/src/socket.rs deleted file mode 100644 index 6b8940e..0000000 --- a/usdpl-core/src/socket.rs +++ /dev/null @@ -1,174 +0,0 @@ -//! Web messaging -use std::io::{Read, Write}; -use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; - -use crate::serdes::{DumpError, Dumpable, LoadError, Loadable}; -use crate::{RemoteCall, RemoteCallResponse}; - -/// Host IP address for web browsers -pub const HOST_STR: &str = "localhost"; -/// Host IP address -pub const HOST: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1); - -/// Standard max packet size -pub const PACKET_BUFFER_SIZE: usize = 1024; -/// Encryption nonce size -pub const NONCE_SIZE: usize = 12; - -/// Address and port -#[inline] -pub fn socket_addr(port: u16) -> SocketAddr { - SocketAddr::V4(SocketAddrV4::new(HOST, port)) -} - -/// Accepted Packet types and the data they contain -pub enum Packet { - /// A remote call - Call(RemoteCall), - /// A reponse to a remote call - CallResponse(RemoteCallResponse), - /// Unused - KeepAlive, - /// Invalid - Invalid, - /// General message - Message(String), - /// Response to an unsupported packet - Unsupported, - /// Broken packet type, useful for testing - Bad, - /// Many packets merged into one - Many(Vec), - /// Translation data dump - Translations(Vec<(String, Vec)>), - /// Request translations for language - Language(String), -} - -impl Packet { - /// Byte representing the packet type -- the first byte of any packet in USDPL - const fn discriminant(&self) -> u8 { - match self { - Self::Call(_) => 1, - Self::CallResponse(_) => 2, - Self::KeepAlive => 3, - Self::Invalid => 4, - Self::Message(_) => 5, - Self::Unsupported => 6, - Self::Bad => 7, - Self::Many(_) => 8, - Self::Translations(_) => 9, - Self::Language(_) => 10, - } - } -} - -impl Loadable for Packet { - fn load(buf: &mut dyn Read) -> Result<(Self, usize), LoadError> { - let mut discriminant_buf = [u8::MAX; 1]; - buf.read_exact(&mut discriminant_buf) - .map_err(LoadError::Io)?; - let mut result: (Self, usize) = match discriminant_buf[0] { - //0 => (None, 0), - 1 => { - let (obj, len) = RemoteCall::load(buf)?; - (Self::Call(obj), len) - } - 2 => { - let (obj, len) = RemoteCallResponse::load(buf)?; - (Self::CallResponse(obj), len) - } - 3 => (Self::KeepAlive, 0), - 4 => (Self::Invalid, 0), - 5 => { - let (obj, len) = String::load(buf)?; - (Self::Message(obj), len) - } - 6 => (Self::Unsupported, 0), - 7 => return Err(LoadError::InvalidData), - 8 => { - let (obj, len) = <_>::load(buf)?; - (Self::Many(obj), len) - } - 9 => { - let (obj, len) = <_>::load(buf)?; - (Self::Translations(obj), len) - } - 10 => { - let (obj, len) = <_>::load(buf)?; - (Self::Language(obj), len) - } - _ => return Err(LoadError::InvalidData), - }; - result.1 += 1; - Ok(result) - } -} - -impl Dumpable for Packet { - fn dump(&self, buf: &mut dyn Write) -> Result { - let size1 = buf.write(&[self.discriminant()]).map_err(DumpError::Io)?; - let result = match self { - Self::Call(c) => c.dump(buf), - Self::CallResponse(c) => c.dump(buf), - Self::KeepAlive => Ok(0), - Self::Invalid => Ok(0), - Self::Message(s) => s.dump(buf), - Self::Unsupported => Ok(0), - Self::Bad => return Err(DumpError::Unsupported), - Self::Many(v) => v.dump(buf), - Self::Translations(tr) => tr.dump(buf), - Self::Language(l) => l.dump(buf), - }?; - Ok(size1 + result) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[cfg(feature = "encrypt")] - #[test] - fn encryption_integration_test() { - let key = - hex_literal::hex!("59C4E408F27250B3147E7724511824F1D28ED7BEF43CF7103ACE747F77A2B265"); - let nonce = [0u8; NONCE_SIZE]; - let packet = Packet::Call(RemoteCall { - id: 42, - function: "test".into(), - parameters: Vec::new(), - }); - let mut buffer = Vec::with_capacity(PACKET_BUFFER_SIZE); - let len = packet.dump_encrypted(&mut buffer, &key, &nonce).unwrap(); - println!( - "buffer: {}", - String::from_utf8(buffer.as_slice()[..len].to_vec()).unwrap() - ); - - let (packet_out, _len) = - Packet::load_encrypted(&buffer.as_slice()[..len], &key, &nonce).unwrap(); - - if let Packet::Call(call_out) = packet_out { - if let Packet::Call(call_in) = packet { - assert_eq!( - call_in.id, call_out.id, - "Input and output packets do not match" - ); - assert_eq!( - call_in.function, call_out.function, - "Input and output packets do not match" - ); - assert_eq!( - call_in.parameters.len(), - call_out.parameters.len(), - "Input and output packets do not match" - ); - } else { - panic!("Packet in not a Call"); - } - } else { - panic!("Packet out not a Call!"); - } - } -} diff --git a/usdpl-front/Cargo.toml b/usdpl-front/Cargo.toml index 942c526..9dfc78c 100644 --- a/usdpl-front/Cargo.toml +++ b/usdpl-front/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "usdpl-front" version = "0.11.0" -authors = ["NGnius (Graham) "] +authors = ["NGnius "] edition = "2021" license = "GPL-3.0-only" -repository = "https://github.com/NGnius/usdpl-rs" +repository = "https://git.ngni.us/NG-SD-Plugins/usdpl-rs" readme = "../README.md" description = "Universal Steam Deck Plugin Library front-end designed for WASM" @@ -14,8 +14,8 @@ crate-type = ["cdylib", "rlib"] [features] default = [] decky = ["usdpl-core/decky"] -debug = ["console_error_panic_hook", "console_log"] -encrypt = ["usdpl-core/encrypt", "obfstr", "hex"] +debug = ["console_error_panic_hook"] +#encrypt = ["usdpl-core/encrypt", "obfstr", "hex"] [dependencies] wasm-bindgen = "0.2" @@ -23,7 +23,6 @@ wasm-bindgen-futures = "0.4" gloo-net = { version = "0.4", features = ["websocket"] } futures = "0.3" futures-channel = "0.3" -console_log = { version = "1.0", optional = true, features = ["color"] } # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires diff --git a/usdpl-front/src/client_handler.rs b/usdpl-front/src/client_handler.rs index 4f3c799..eff4991 100644 --- a/usdpl-front/src/client_handler.rs +++ b/usdpl-front/src/client_handler.rs @@ -32,23 +32,19 @@ async fn send_recv_ws<'a>(mut tx: futures_channel::mpsc::Sender { - #[cfg(feature = "debug")] - web_sys::console::debug_1(&format!("Got message to send over websocket").into()); + log::debug!("Got message to send over websocket"); if let Some(next) = next { match next { Ok(next) => { @@ -65,8 +61,7 @@ async fn send_recv_ws<'a>(mut tx: futures_channel::mpsc::Sender { - #[cfg(feature = "debug")] - web_sys::console::debug_1(&format!("Received message from websocket").into()); + log::debug!("Received message from websocket"); if let Some(next) = response { match next { Ok(Message::Bytes(b)) => tx.send(Ok(b.into())).await.unwrap_or(()), @@ -84,11 +79,9 @@ async fn send_recv_ws<'a>(mut tx: futures_channel::mpsc::Sender tx.send(Ok(b.into())).await.unwrap_or(()), Ok(_) => tx.send(Err("Message::Text not allowed".into())).await.unwrap_or(()), @@ -104,11 +97,9 @@ async fn send_recv_ws<'a>(mut tx: futures_channel::mpsc::Sender { if let Err(e) = ws_sink.send(Message::Bytes(next.into())).await { @@ -129,38 +120,7 @@ async fn send_recv_ws<'a>(mut tx: futures_channel::mpsc::Sender { - if let Err(e) = ws.send(Message::Bytes(next.into())).await { - tx2.send(Err(e.to_string())).await.unwrap_or(()); - } - }, - Err(e) => tx2.send(Err(e.to_string())).await.unwrap_or(()) - } - } else { - break; - } - } - }); - - spawn_local(async move { - while let State::Open = ws.state() { - if let Some(next) = ws.next().await { - match next { - Ok(Message::Bytes(b)) => tx.send(Ok(b.into())).await.unwrap_or(()), - Ok(_) => tx.send(Err("Message::Text not allowed".into())).await.unwrap_or(()), - Err(e) => tx.send(Err(e.to_string())).await.unwrap_or(()), - } - } else { - break; - } - } - });*/ + log::debug!("ws with url `{}` has closed", url); } #[derive(Debug)] @@ -197,8 +157,7 @@ impl ClientHandler<'static> for WebSocketHandler { "ws://usdpl-ws-{}.localhost:{}/{}.{}/{}", id, self.port, package, service, method, ); - #[cfg(feature = "debug")] - web_sys::console::log_1(&format!("doing send/receive on ws url `{}`", url).into()); + log::debug!("doing send/receive on ws url `{}`", url); let (tx, rx) = futures_channel::mpsc::channel(CHANNEL_BOUND); spawn_local(send_recv_ws(tx, url, input)); diff --git a/usdpl-front/src/connection.rs b/usdpl-front/src/connection.rs deleted file mode 100644 index 442e312..0000000 --- a/usdpl-front/src/connection.rs +++ /dev/null @@ -1,126 +0,0 @@ -//use std::net::TcpStream; -//use std::io::{Read, Write}; - -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::JsFuture; - -//use web_sys::{WebSocket, MessageEvent, ErrorEvent}; -use js_sys::JsString; -use web_sys::{Request, RequestInit, RequestMode, Response}; -//use wasm_rs_shared_channel::{Expects, spsc::{Receiver, Sender}}; - -use usdpl_core::serdes::{Dumpable, Loadable, Primitive}; -use usdpl_core::socket; - -#[cfg(feature = "encrypt")] -const NONCE: [u8; socket::NONCE_SIZE] = [0u8; socket::NONCE_SIZE]; - -pub async fn send_recv_packet( - id: u64, - packet: socket::Packet, - port: u16, - #[cfg(feature = "encrypt")] key: Vec, -) -> Result { - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.mode(RequestMode::Cors); - - let url = format!( - "http://usdpl{}.{}:{}/usdpl/call", - id, - socket::HOST_STR, - port - ); - - #[allow(unused_variables)] - let (buffer, len) = dump_to_buffer( - packet, - #[cfg(feature = "encrypt")] - key.as_slice(), - )?; - let string: String = String::from_utf8_lossy(buffer.as_slice()).into(); - #[cfg(feature = "debug")] - crate::imports::console_log(&format!("Dumped base64 `{}` len:{}", string, len)); - opts.body(Some(&string.into())); - - let request = Request::new_with_str_and_init(&url, &opts)?; - - //request.headers().set("Accept", "text/base64")?; - //.set("Authorization", "wasm TODO_KEY")?; - - let window = web_sys::window().unwrap(); - let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; - - let resp: Response = resp_value.dyn_into()?; - let text = JsFuture::from(resp.text()?).await?; - let string: JsString = text.dyn_into()?; - - let rust_str = string.as_string().unwrap(); - #[cfg(feature = "debug")] - crate::imports::console_log(&format!( - "Received base64 `{}` len:{}", - rust_str, - rust_str.len() - )); - - #[cfg(not(feature = "encrypt"))] - { - Ok(socket::Packet::load_base64(rust_str.as_bytes()) - .map_err(super::convert::str_to_js)? - .0) - } - - #[cfg(feature = "encrypt")] - { - Ok( - socket::Packet::load_encrypted(rust_str.as_bytes(), key.as_slice(), &NONCE) - .map_err(super::convert::str_to_js)? - .0, - ) - } -} - -pub async fn send_call( - id: u64, - packet: socket::Packet, - port: u16, - #[cfg(feature = "encrypt")] key: Vec, -) -> Result, JsValue> { - let packet = send_recv_packet( - id, - packet, - port, - #[cfg(feature = "encrypt")] - key, - ) - .await?; - - match packet { - socket::Packet::CallResponse(resp) => Ok(resp.response), - _ => { - //imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", resp.url())); - Err("Expected call response message, got something else".into()) - } - } -} - -#[cfg(feature = "encrypt")] -fn dump_to_buffer(packet: socket::Packet, key: &[u8]) -> Result<(Vec, usize), JsValue> { - let mut buffer = Vec::with_capacity(socket::PACKET_BUFFER_SIZE); - //buffer.extend_from_slice(&[0u8; socket::PACKET_BUFFER_SIZE]); - let len = packet - .dump_encrypted(&mut buffer, key, &NONCE) - .map_err(super::convert::str_to_js)?; - Ok((buffer, len)) -} - -#[cfg(not(feature = "encrypt"))] -fn dump_to_buffer(packet: socket::Packet) -> Result<(Vec, usize), JsValue> { - let mut buffer = String::with_capacity(socket::PACKET_BUFFER_SIZE); - //buffer.extend_from_slice(&[0u8; socket::PACKET_BUFFER_SIZE]); - let len = packet - .dump_base64(&mut buffer) - .map_err(super::convert::str_to_js)?; - Ok((buffer.as_bytes().to_vec(), len)) -} diff --git a/usdpl-front/src/console_logs.rs b/usdpl-front/src/console_logs.rs new file mode 100644 index 0000000..2b14974 --- /dev/null +++ b/usdpl-front/src/console_logs.rs @@ -0,0 +1,52 @@ +pub(crate) struct BuiltInLogger { + min_level: log::Level, +} + +impl BuiltInLogger { + pub const fn new(min: log::Level) -> Self { + Self { + min_level: min, + } + } +} + +impl log::Log for BuiltInLogger { + fn enabled(&self, metadata: &log::Metadata) -> bool { + metadata.level() <= self.min_level + } + + fn log(&self, record: &log::Record) { + if self.enabled(record.metadata()) { + match record.level() { + log::Level::Error => web_sys::console::error_1(&fmt_msg(record).into()), + log::Level::Warn => web_sys::console::warn_1(&fmt_msg(record).into()), + log::Level::Info => web_sys::console::log_1(&fmt_msg(record).into()), + log::Level::Debug => web_sys::console::debug_1(&fmt_msg(record).into()), + log::Level::Trace => web_sys::console::debug_1(&fmt_msg(record).into()), + } + } + } + + fn flush(&self) {} +} + +fn fmt_msg(record: &log::Record) -> String { + #[cfg(feature = "debug")] + { format!("[{}]({}) {}", record.level(), file_line_info(record), record.args()) } + #[cfg(not(feature = "debug"))] + { format!("[{}]({}) {}", record.level(), module_line_info(record), record.args()) } +} + +#[cfg(feature = "debug")] +fn file_line_info(record: &log::Record) -> String { + let filepath = record.file().unwrap_or(""); + let line = record.line().map(|l| l.to_string()).unwrap_or_else(|| "line?".to_string()); + format!("{}:{}", filepath, line) +} + +#[cfg(not(feature = "debug"))] +fn module_line_info(record: &log::Record) -> String { + let target = record.target(); + let line = record.line().map(|l| l.to_string()).unwrap_or_else(|| "line?".to_string()); + format!("{}:{}", target, line) +} diff --git a/usdpl-front/src/lib.rs b/usdpl-front/src/lib.rs index aa2fd5d..ebaf687 100644 --- a/usdpl-front/src/lib.rs +++ b/usdpl-front/src/lib.rs @@ -7,15 +7,10 @@ mod client_handler; pub use client_handler::WebSocketHandler; -//mod connection; +mod console_logs; mod convert; pub mod wasm; -/*#[allow(missing_docs)] // existence is pain otherwise -pub mod _nrpc_js_interop { - include!(concat!(env!("OUT_DIR"), "/mod.rs")); -}*/ - #[allow(missing_docs)] pub mod _helpers { pub use js_sys; @@ -26,21 +21,14 @@ pub mod _helpers { pub use nrpc; } -//use std::sync::atomic::{AtomicU64, Ordering}; - -//use js_sys::Array; use wasm_bindgen::prelude::*; -//use usdpl_core::{socket::Packet, RemoteCall}; -//const REMOTE_CALL_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); -//const REMOTE_PORT: std::sync::atomic::AtomicU16 = std::sync::atomic::AtomicU16::new(31337); +#[cfg(feature = "debug")] +const DEFAULT_MIN_LEVEL: log::Level = log::Level::Trace; +#[cfg(not(feature = "debug"))] +const DEFAULT_MIN_LEVEL: log::Level = log::Level::Info; -/*static mut CTX: UsdplContext = UsdplContext { - port: 0, - //id: AtomicU64::new(0), - #[cfg(feature = "encrypt")] - key: Vec::new(), -};*/ +const DEFAULT_LOGGER: console_logs::BuiltInLogger = console_logs::BuiltInLogger::new(DEFAULT_MIN_LEVEL); static mut CACHE: Option> = None; @@ -51,56 +39,40 @@ fn encryption_key() -> Vec { hex::decode(obfstr::obfstr!(env!("USDPL_ENCRYPTION_KEY"))).unwrap() } -/* -//#[wasm_bindgen] -#[derive(Debug)] -struct UsdplContext { - port: u16, - id: AtomicU64, - #[cfg(feature = "encrypt")] - key: Vec, -} - -fn get_port() -> u16 { - unsafe { CTX.port } -} - -#[cfg(feature = "encrypt")] -fn get_key() -> Vec { - unsafe { CTX.key.clone() } -}*/ - -/*fn increment_id() -> u64 { - let atomic = unsafe { &CTX.id }; - atomic.fetch_add(1, Ordering::SeqCst) -}*/ +static INIT_DONE: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); /// Initialize the front-end library -#[wasm_bindgen] -pub fn init_usdpl(port: u16) { - #[cfg(feature = "debug")] - web_sys::console::log_1(&format!("init_usdpl(port={})", port).into()); - #[cfg(feature = "console_error_panic_hook")] - console_error_panic_hook::set_once(); - #[cfg(feature = "console_log")] - console_log::init_with_level(log::Level::Debug).expect("USDPL: error initializing console log"); +//#[wasm_bindgen] +pub fn init_usdpl() { + if !INIT_DONE.swap(true, std::sync::atomic::Ordering::SeqCst) { + #[cfg(feature = "console_error_panic_hook")] + console_error_panic_hook::set_once(); - /*unsafe { - CTX = UsdplContext { - port: port, - //id: AtomicU64::new(0), - #[cfg(feature = "encrypt")] - key: encryption_key(), - }; - }*/ + log::set_logger(&DEFAULT_LOGGER) + .map_err(|e| web_sys::console::error_1(&format!("Failed to setup USDPL logger: {}", e).into())) + .unwrap_or(()); + log::set_max_level(log::LevelFilter::Trace); + log::debug!("init_usdpl() log configured"); - unsafe { - CACHE = Some(std::collections::HashMap::new()); + unsafe { + CACHE = Some(std::collections::HashMap::new()); + } + + log::info!("USDPL init succeeded: {}", build_info()); + } else { + log::info!("USDPL init was re-attempted"); } +} - #[cfg(feature = "debug")] - web_sys::console::log_1(&format!("USDPL:{} init succeeded", port).into()); - log::info!("USDPL:{} init succeeded", port); +fn build_info() -> String { + format!("{} v{} ({}) for {} by {}, more: {}", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION"), + env!("CARGO_PKG_LICENSE"), + target_usdpl(), + env!("CARGO_PKG_AUTHORS"), + env!("CARGO_PKG_REPOSITORY"), + ) } /// Get the targeted plugin framework, or "any" if unknown @@ -140,92 +112,6 @@ pub fn get_value(key: String) -> JsValue { } } -/* -/// Call a function on the back-end. -/// Returns null (None) if this fails for any reason. -#[wasm_bindgen] -pub async fn call_backend(name: String, parameters: Vec) -> JsValue { - #[cfg(feature = "debug")] - web_sys::console::log_1(&format!( - "call_backend({}, [params; {}])", - name, - parameters.len() - ).into()); - let next_id = increment_id(); - let mut params = Vec::with_capacity(parameters.len()); - for val in parameters { - params.push(convert::js_to_primitive(val)); - } - let port = get_port(); - #[cfg(feature = "debug")] - web_sys::console::log_1(&format!("USDPL: Got port {}", port).into()); - let results = connection::send_call( - next_id, - Packet::Call(RemoteCall { - id: next_id, - function: name.clone(), - parameters: params, - }), - port, - #[cfg(feature = "encrypt")] - get_key(), - ) - .await; - let results = match results { - Ok(x) => x, - #[allow(unused_variables)] - Err(e) => { - #[cfg(feature = "debug")] - web_sys::console::error_1(&format!("USDPL: Got error while calling {}: {:?}", name, e).into()); - return JsValue::NULL; - } - }; - let results_js = Array::new_with_length(results.len() as _); - let mut i = 0; - for item in results { - results_js.set(i as _, convert::primitive_to_js(item)); - i += 1; - } - results_js.into() -} - -/// Initialize translation strings for the front-end -#[wasm_bindgen] -pub async fn init_tr(locale: String) { - let next_id = increment_id(); - match connection::send_recv_packet( - next_id, - Packet::Language(locale.clone()), - get_port(), - #[cfg(feature = "encrypt")] - get_key(), - ) - .await - { - Ok(Packet::Translations(translations)) => { - #[cfg(feature = "debug")] - web_sys::console::log_1(&format!("USDPL: Got translations for {}", locale).into()); - // convert translations into map - let mut tr_map = std::collections::HashMap::with_capacity(translations.len()); - for (key, val) in translations { - tr_map.insert(key, val); - } - unsafe { TRANSLATIONS = Some(tr_map) } - } - Ok(_) => { - #[cfg(feature = "debug")] - web_sys::console::error_1(&format!("USDPL: Got wrong packet response for init_tr").into()); - unsafe { TRANSLATIONS = None } - } - #[allow(unused_variables)] - Err(e) => { - #[cfg(feature = "debug")] - web_sys::console::error_1(&format!("USDPL: Got wrong error for init_tr: {:#?}", e).into()); - unsafe { TRANSLATIONS = None } - } - } -}*/ - /// Translate a phrase, equivalent to tr_n(msg_id, 0) #[wasm_bindgen] pub fn tr(msg_id: String) -> String { From 1ad620506707065da3c08be7c237ba40a921a317 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Mon, 4 Sep 2023 11:59:35 -0400 Subject: [PATCH 11/12] Add built-in back implementations for USDPL services, disable code gen for them by default (only usdpl-back needs to generate them) --- usdpl-back/build.rs | 6 ++++ usdpl-back/src/lib.rs | 8 +++++ usdpl-back/src/rpc/registry.rs | 16 ++++------ usdpl-back/src/services_impl/dev_tools.rs | 22 ++++++++++++++ usdpl-back/src/services_impl/mod.rs | 5 ++++ usdpl-back/src/services_impl/translations.rs | 31 ++++++++++++++++++++ usdpl-back/src/websockets.rs | 2 +- usdpl-build/src/back/mod.rs | 10 +++++++ 8 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 usdpl-back/build.rs create mode 100644 usdpl-back/src/services_impl/dev_tools.rs create mode 100644 usdpl-back/src/services_impl/mod.rs create mode 100644 usdpl-back/src/services_impl/translations.rs diff --git a/usdpl-back/build.rs b/usdpl-back/build.rs new file mode 100644 index 0000000..2e3439b --- /dev/null +++ b/usdpl-back/build.rs @@ -0,0 +1,6 @@ +fn main() { + usdpl_build::back::build_with_custom_builtins( + [].into_iter(), + [].into_iter(), + ) +} diff --git a/usdpl-back/src/lib.rs b/usdpl-back/src/lib.rs index 769b0a6..369a985 100644 --- a/usdpl-back/src/lib.rs +++ b/usdpl-back/src/lib.rs @@ -12,6 +12,7 @@ mod api_common; mod api_decky; mod rpc; +mod services_impl; //mod errors; mod websockets; @@ -19,6 +20,13 @@ mod websockets; pub use websockets::WebsocketServer as Server; //pub use errors::{ServerError, ServerResult}; + +#[allow(missing_docs)] +#[allow(dead_code)] +pub(crate) mod services { + include!(concat!(env!("OUT_DIR"), "/mod.rs")); +} + /// USDPL backend API. /// This contains functionality used exclusively by the back-end. pub mod api { diff --git a/usdpl-back/src/rpc/registry.rs b/usdpl-back/src/rpc/registry.rs index 26cd152..3ca2ee8 100644 --- a/usdpl-back/src/rpc/registry.rs +++ b/usdpl-back/src/rpc/registry.rs @@ -12,15 +12,6 @@ pub struct ServiceRegistry<'a> { } impl<'a> ServiceRegistry<'a> { - /*pub async fn call(&self, package: &str, service: &str, method: &str, data: bytes::Bytes) -> Result { - let key = Self::descriptor(package, service); - self.call_descriptor(&key, method, data).await - } - - fn descriptor(package: &str, service: &str) -> String { - format!("{}.{}", package, service) - }*/ - pub async fn call_descriptor<'b: 'a>( &mut self, descriptor: &str, @@ -43,7 +34,10 @@ impl<'a> ServiceRegistry<'a> { self } - pub fn new() -> Self { - Self::default() + pub fn with_builtins() -> Self { + let mut reg = Self::default(); + reg.register(crate::services::usdpl::DevToolsServer::new(crate::services_impl::DevTools{})) + .register(crate::services::usdpl::TranslationsServer::new(crate::services_impl::Translations{})); + reg } } diff --git a/usdpl-back/src/services_impl/dev_tools.rs b/usdpl-back/src/services_impl/dev_tools.rs new file mode 100644 index 0000000..1be811f --- /dev/null +++ b/usdpl-back/src/services_impl/dev_tools.rs @@ -0,0 +1,22 @@ +use crate::services::usdpl as generated; + +/// Built-in dev tools service implementation +pub(crate) struct DevTools {} + +#[async_trait::async_trait] +impl<'a> generated::IDevTools<'a> for DevTools { + async fn log( + &mut self, + input: generated::LogMessage, + ) -> Result> { + match input.level { + lvl if lvl == generated::LogLevel::Trace as _ => log::trace!("{}", input.msg), + lvl if lvl == generated::LogLevel::Debug as _ => log::debug!("{}", input.msg), + lvl if lvl == generated::LogLevel::Info as _ => log::info!("{}", input.msg), + lvl if lvl == generated::LogLevel::Warn as _ => log::warn!("{}", input.msg), + lvl if lvl == generated::LogLevel::Error as _ => log::error!("{}", input.msg), + lvl => return Err(Box::::from(format!("Unexpected input log level {}", lvl))) + } + Ok(generated::Empty{ ok: true }) + } +} diff --git a/usdpl-back/src/services_impl/mod.rs b/usdpl-back/src/services_impl/mod.rs new file mode 100644 index 0000000..7e3aa3a --- /dev/null +++ b/usdpl-back/src/services_impl/mod.rs @@ -0,0 +1,5 @@ +mod dev_tools; +pub(crate) use dev_tools::DevTools; + +mod translations; +pub(crate) use translations::Translations; diff --git a/usdpl-back/src/services_impl/translations.rs b/usdpl-back/src/services_impl/translations.rs new file mode 100644 index 0000000..d51734e --- /dev/null +++ b/usdpl-back/src/services_impl/translations.rs @@ -0,0 +1,31 @@ +use crate::services::usdpl as generated; + +/// Built-in translation service implementation +pub(crate) struct Translations {} + +#[async_trait::async_trait] +impl<'a> generated::ITranslations<'a> for Translations { + async fn get_language( + &mut self, + input: generated::LanguageRequest, + ) -> Result> { + let catalog = load_locale(&input.lang).map_err(|e| Box::new(e) as _)?; + let catalog_map = catalog.nalltext(); + let mut map = std::collections::HashMap::with_capacity(catalog_map.len()); + for (key, val) in catalog_map.into_iter() { + if val.len() > 1 { + log::warn!("Translations key {} for language {} has plural entries which aren't currently supported", key, input.lang); + } + if let Some(val_0) = val.get(0) { + map.insert(key.to_owned(), val_0.to_owned()); + } + } + Ok(generated::TranslationsReply { translations: map }) + } +} + +fn load_locale(lang: &str) -> Result { + let path = crate::api::dirs::plugin().unwrap_or_else(|| "".into()).join("translations").join(format!("{}.mo", lang)); + let file = std::fs::File::open(path).map_err(|e| gettext_ng::Error::Io(e))?; + gettext_ng::Catalog::parse(file) +} diff --git a/usdpl-back/src/websockets.rs b/usdpl-back/src/websockets.rs index 7be05d0..142226b 100644 --- a/usdpl-back/src/websockets.rs +++ b/usdpl-back/src/websockets.rs @@ -21,7 +21,7 @@ impl WebsocketServer { /// Initialise an instance of the back-end websocket server pub fn new(port_usdpl: u16) -> Self { Self { - services: StaticServiceRegistry::new(), + services: StaticServiceRegistry::with_builtins(), port: port_usdpl, } } diff --git a/usdpl-build/src/back/mod.rs b/usdpl-build/src/back/mod.rs index ec1b904..b5683e2 100644 --- a/usdpl-build/src/back/mod.rs +++ b/usdpl-build/src/back/mod.rs @@ -1,6 +1,16 @@ pub fn build( custom_protos: impl Iterator, custom_dirs: impl Iterator, +) { + nrpc_build::compile_servers( + custom_protos, + crate::proto_out_paths(custom_dirs), + ) +} + +pub fn build_with_custom_builtins( + custom_protos: impl Iterator, + custom_dirs: impl Iterator, ) { crate::dump_protos_out().unwrap(); nrpc_build::compile_servers( From 4aa33971b440f38efdb3757389d30caa2ee4d9e8 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Mon, 9 Oct 2023 18:24:41 -0400 Subject: [PATCH 12/12] Fix server streaming codegen --- usdpl-build/src/front/service_generator.rs | 51 +++++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/usdpl-build/src/front/service_generator.rs b/usdpl-build/src/front/service_generator.rs index d3dd5f9..a4201f1 100644 --- a/usdpl-build/src/front/service_generator.rs +++ b/usdpl-build/src/front/service_generator.rs @@ -171,19 +171,38 @@ fn generate_service_methods( gen_methods.push(quote::quote! { #[wasm_bindgen] - pub async fn #method_name(&self, #(#input_params)*, callback: js_sys::Function) { - + pub async fn #method_name(&self, #(#input_params)* callback: js_sys::Function) { #params_to_fields_transformer match self.service.#method_name(val.into()).await { - Ok(x) => { + Ok(mut x) => { while let Some(next_result) = x.next().await { match next_result { Err(e) => { log::error!("service:{}|method:{}|error:{}", self.service.descriptor(), #method_name_str, e); }, Ok(item) => { - callback.call1(JsValue::undefined(), item.into_wasm_streamable()); + #[inline(always)] + fn js_to_string(js: &JsValue) -> String { + if let Some(s) = js.as_string() { + s + } else { + format!("{:?}", js) + } + } + let item: #method_output = item.into(); + match callback.call1(&JsValue::undefined(), &item.into_wasm_streamable()) { + Ok(js_val) => { + if !(js_val.is_undefined() && js_val.is_null()) { + let str_val = js_to_string(&js_val); + log::info!("service:{}|method:{}|callback result:{}", self.service.descriptor(), #method_name_str, str_val); + } + }, + Err(js_e) => { + let str_e = js_to_string(&js_e); + log::error!("service:{}|method:{}|error:{}", self.service.descriptor(), #method_name_str, str_e); + } + } } } } @@ -206,14 +225,34 @@ fn generate_service_methods( let stream = Box::new(::usdpl_front::wasm::JsFunctionStream::<#method_input>::from_function(generator)); match self.service.#method_name(stream).await { - Ok(x) => { + Ok(mut x) => { while let Some(next_result) = x.next().await { match next_result { Err(e) => { log::error!("service:{}|method:{}|error:{}", self.service.descriptor(), #method_name_str, e); }, Ok(item) => { - callback.call1(JsValue::undefined(), item.into_wasm_streamable()); + #[inline(always)] + fn js_to_string(js: &JsValue) -> String { + if let Some(s) = js.as_string() { + s + } else { + format!("{:?}", js) + } + } + let item: #method_output = item.into(); + match callback.call1(&JsValue::undefined(), &item.into_wasm_streamable()) { + Ok(js_val) => { + if !(js_val.is_undefined() && js_val.is_null()) { + let str_val = js_to_string(&js_val); + log::info!("service:{}|method:{}|callback result:{}", self.service.descriptor(), #method_name_str, str_val); + } + }, + Err(js_e) => { + let str_e = js_to_string(&js_e); + log::error!("service:{}|method:{}|error:{}", self.service.descriptor(), #method_name_str, str_e); + } + } } } }