diff --git a/Windows/Rust/CubeLib/Cargo.lock b/Windows/Rust/CubeLib/Cargo.lock new file mode 100644 index 0000000..67aec61 --- /dev/null +++ b/Windows/Rust/CubeLib/Cargo.lock @@ -0,0 +1,1455 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cube_lib" +version = "0.1.0" +dependencies = [ + "async-trait", + "futures-util", + "http", + "serde", + "serde_json", + "tokio", + "tokio-test", + "tokio-tungstenite", + "tracing", + "tracing-subscriber", + "url", +] + +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.184" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "openssl" +version = "0.10.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-test" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6d24790a10a7af737693a3e8f1d03faef7e6ca0cc99aae5066f533766de545" +dependencies = [ + "futures-core", + "tokio", + "tokio-stream", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tokio-native-tls", + "tungstenite", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Windows/Rust/CubeLib/Cargo.toml b/Windows/Rust/CubeLib/Cargo.toml new file mode 100644 index 0000000..6be88d3 --- /dev/null +++ b/Windows/Rust/CubeLib/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "cube_lib" +version = "0.1.0" +edition = "2021" +description = "CubeLib - Rust library for WebSocket communication and utilities" +authors = ["JoyD Team"] + +[dependencies] +# WebSocket +tokio-tungstenite = { version = "0.21", features = ["tokio-native-tls"] } +futures-util = { version = "0.3", default-features = false, features = ["sink", "std", "async-await"] } +tokio = { version = "1", features = ["full"] } + +# Serialization +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +# Async runtime +async-trait = "0.1" + +# Logging +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } + +# HTTP headers +http = "1.0" + +# URL parsing +url = "2.4" + +[dev-dependencies] +tokio-test = "0.4" diff --git a/Windows/Rust/CubeLib/README.md b/Windows/Rust/CubeLib/README.md new file mode 100644 index 0000000..1c52010 --- /dev/null +++ b/Windows/Rust/CubeLib/README.md @@ -0,0 +1,107 @@ +# CubeLib - Rust WebSocket Library + +Rust 版本的 CubeLib,提供 WebSocket 通信功能。 + +## 结构 + +``` +CubeLib/ +├── Cargo.toml +├── src/ +│ ├── lib.rs # 库入口 +│ └── websocket/ +│ ├── mod.rs # 模块定义 +│ ├── client.rs # WebSocket 客户端 +│ ├── config.rs # 配置类 +│ ├── message.rs # 消息类型 +│ └── error.rs # 错误类型 +└── README.md +``` + +## 功能特性 + +- **事件驱动**:支持 Connected、Disconnected、Message、Error 等回调 +- **自动重连**:支持指数退避的重连机制 +- **自定义头**:支持设置 ClientType 等 HTTP 头 +- **心跳保活**:可配置的心跳间隔 +- **消息队列**:离线时自动缓存消息,连接后自动发送 +- **调试模式**:可选的日志输出 + +## 快速开始 + +```rust +use cube_lib::websocket::{WebSocketClient, WebSocketConfig}; + +#[tokio::main] +async fn main() { + let config = WebSocketConfig::new("ws://localhost:8087/ws") + .with_client_type("Updater") + .with_debug(true); + + let mut client = WebSocketClient::new(config); + + // 设置事件回调 + client.on_connected(|url| { + println!("Connected to: {}", url); + }); + + client.on_message(|msg_type, data| { + println!("Received [{}]: {:?}", msg_type, data); + }); + + client.on_disconnected(|| { + println!("Disconnected"); + }); + + // 连接 + client.connect().await; + + // 发送消息(格式与 MasterSuite 后端兼容) + client.send("ping", serde_json::json!({ + "timestamp": "2024-01-01 00:00:00" + })).await; + + // 保持运行 + tokio::signal::ctrl_c().await.ok(); + client.disconnect().await; +} +``` + +## WebSocketConfig 配置项 + +| 字段 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| `ws_url` | String | `ws://localhost:8087/ws` | 服务器地址 | +| `auto_connect` | bool | `true` | 初始化时自动连接 | +| `reconnect` | bool | `true` | 断开时自动重连 | +| `max_reconnect_delay_ms` | u64 | `30000` | 最大重连延迟(毫秒) | +| `reconnect_delay_ms` | u64 | `1000` | 初始重连延迟(毫秒) | +| `debug_mode` | bool | `false` | 调试模式 | +| `heartbeat_interval_ms` | u64 | `30000` | 心跳间隔(毫秒),0=禁用 | +| `connect_timeout_ms` | u64 | `10000` | 连接超时(毫秒) | +| `max_queue_size` | usize | `100` | 消息队列最大长度 | +| `client_type` | Option\ | `None` | 客户端类型标识 | +| `custom_headers` | Vec\<(String, String)\> | `[]` | 自定义 HTTP 头 | + +## 消息格式 + +与 MasterSuite 后端兼容的 JSON 格式: + +```json +{ + "Type": "message_type", + "Data": { ... }, + "MessageId": "optional_id" +} +``` + +## 集成到项目 + +在 `Cargo.toml` 中添加: + +```toml +[dependencies] +cube_lib = { path = "../Rust/CubeLib" } +``` + +或从 Git 引用(待发布后)。 diff --git a/Windows/Rust/CubeLib/src/lib.rs b/Windows/Rust/CubeLib/src/lib.rs new file mode 100644 index 0000000..e9d6450 --- /dev/null +++ b/Windows/Rust/CubeLib/src/lib.rs @@ -0,0 +1,33 @@ +//! # CubeLib +//! +//! Rust library providing WebSocket communication and utility components. +//! +//! ## Modules +//! +//! - [`websocket`] - WebSocket client and server implementations +//! +//! ## Quick Start +//! +//! ```rust,no_run +//! use cube_lib::websocket::{WebSocketClient, WebSocketConfig}; +//! +//! #[tokio::main] +//! async fn main() { +//! let config = WebSocketConfig::default(); +//! let mut client = WebSocketClient::new(config); +//! +//! client.on_connected(|_| { +//! println!("Connected!"); +//! }); +//! +//! client.on_message(|_, msg| { +//! println!("Received: {:?}", msg); +//! }); +//! +//! client.connect().await; +//! } +//! ``` + +pub mod websocket; + +pub use websocket::{WebSocketClient, WebSocketConfig, WebSocketMessage}; diff --git a/Windows/Rust/CubeLib/src/websocket/client.rs b/Windows/Rust/CubeLib/src/websocket/client.rs new file mode 100644 index 0000000..daf1657 --- /dev/null +++ b/Windows/Rust/CubeLib/src/websocket/client.rs @@ -0,0 +1,638 @@ +//! WebSocket client implementation + +use crate::websocket::{WebSocketConfig, WebSocketMessage, WebSocketError}; +use futures_util::{SinkExt, StreamExt}; +use std::sync::Arc; +use tokio::sync::{mpsc, Mutex}; +use tokio::time::Duration; +use tracing::{warn, debug}; +use serde_json::Value; + +/// Message to send through the channel +#[derive(Debug)] +enum OutgoingMessage { + /// Text message + Text(String), + /// Binary message + Binary(Vec), + /// Close connection + Close, +} + +/// Connection status +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[allow(clippy::upper_case_acronyms)] +pub enum ConnectionStatus { + /// Disconnected + Disconnected, + /// Connecting + Connecting, + /// Connected + Connected, + /// Reconnecting + Reconnecting, + /// Error + Error, +} + +impl std::fmt::Display for ConnectionStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ConnectionStatus::Disconnected => write!(f, "disconnected"), + ConnectionStatus::Connecting => write!(f, "connecting"), + ConnectionStatus::Connected => write!(f, "connected"), + ConnectionStatus::Reconnecting => write!(f, "reconnecting"), + ConnectionStatus::Error => write!(f, "error"), + } + } +} + +/// Event callback types (wrapped in Arc for cloneability) +pub type ConnectedCallback = Arc; +pub type DisconnectedCallback = Arc; +pub type MessageCallback = Arc; +pub type BinaryCallback = Arc) + Send + Sync>; +pub type ErrorCallback = Arc; +pub type StatusCallback = Arc; +pub type SentCallback = Arc; + +/// WebSocket client with event-driven architecture +pub struct WebSocketClient { + config: WebSocketConfig, + status: Arc>, + sender: Arc>>>, + message_queue: Arc>>, + task_handle: Arc>>>, + is_running: Arc>, + + // Event callbacks + on_connected: Option, + on_disconnected: Option, + on_message: Option, + on_binary: Option, + on_error: Option, + on_status_changed: Option, + on_message_sent: Option, + + // Reconnection state + reconnect_attempts: Arc>, + reconnect_delay_ms: Arc>, +} + +impl WebSocketClient { + /// Create a new WebSocket client + pub fn new(config: WebSocketConfig) -> Self { + Self { + config, + status: Arc::new(Mutex::new(ConnectionStatus::Disconnected)), + sender: Arc::new(Mutex::new(None)), + message_queue: Arc::new(Mutex::new(Vec::new())), + task_handle: Arc::new(Mutex::new(None)), + is_running: Arc::new(Mutex::new(false)), + on_connected: None, + on_disconnected: None, + on_message: None, + on_binary: None, + on_error: None, + on_status_changed: None, + on_message_sent: None, + reconnect_attempts: Arc::new(Mutex::new(0)), + reconnect_delay_ms: Arc::new(Mutex::new(1000)), + } + } + + /// Create a simple client with just the URL + pub fn simple(url: impl Into) -> Self { + Self::new(WebSocketConfig::new(url)) + } + + // ==================== Event Handlers ==================== + + /// Set callback for connected event + pub fn on_connected(&mut self, callback: F) -> &mut Self + where + F: Fn(String) + Send + Sync + 'static, + { + self.on_connected = Some(Arc::new(callback)); + self + } + + /// Set callback for disconnected event + pub fn on_disconnected(&mut self, callback: F) -> &mut Self + where + F: Fn() + Send + Sync + 'static, + { + self.on_disconnected = Some(Arc::new(callback)); + self + } + + /// Set callback for text message received + pub fn on_message(&mut self, callback: F) -> &mut Self + where + F: Fn(String, Value) + Send + Sync + 'static, + { + self.on_message = Some(Arc::new(callback)); + self + } + + /// Set callback for binary message received + pub fn on_binary(&mut self, callback: F) -> &mut Self + where + F: Fn(Vec) + Send + Sync + 'static, + { + self.on_binary = Some(Arc::new(callback)); + self + } + + /// Set callback for error + pub fn on_error(&mut self, callback: F) -> &mut Self + where + F: Fn(String) + Send + Sync + 'static, + { + self.on_error = Some(Arc::new(callback)); + self + } + + /// Set callback for status changed + pub fn on_status_changed(&mut self, callback: F) -> &mut Self + where + F: Fn(ConnectionStatus, ConnectionStatus) + Send + Sync + 'static, + { + self.on_status_changed = Some(Arc::new(callback)); + self + } + + /// Set callback for message sent + pub fn on_message_sent(&mut self, callback: F) -> &mut Self + where + F: Fn(String, Value) + Send + Sync + 'static, + { + self.on_message_sent = Some(Arc::new(callback)); + self + } + + // ==================== Connection Management ==================== + + /// Connect to the WebSocket server + pub async fn connect(&mut self) { + let url = self.config.ws_url.clone(); + self.connect_with_url(&url).await; + } + + /// Connect to a specific URL (overrides config URL) + pub async fn connect_with_url(&mut self, url: &str) { + let _old_status = *self.status.lock().await; + self.set_status(ConnectionStatus::Connecting).await; + + if self.config.debug_mode { + debug!("Connecting to WebSocket server: {}", url); + } + + // Create channel for outgoing messages + let (tx, rx) = mpsc::channel::(100); + *self.sender.lock().await = Some(tx); + + // Clone shared state for the task + let url = url.to_string(); + let config = self.config.clone(); + let status = Arc::clone(&self.status); + let queue = Arc::clone(&self.message_queue); + let is_running = Arc::clone(&self.is_running); + let reconnect_attempts = Arc::clone(&self.reconnect_attempts); + let reconnect_delay_ms = Arc::clone(&self.reconnect_delay_ms); + + // Callbacks + let on_connected = self.on_connected.clone(); + let on_disconnected = self.on_disconnected.clone(); + let on_message = self.on_message.clone(); + let on_binary = self.on_binary.clone(); + let on_error = self.on_error.clone(); + + // Spawn the WebSocket task + *self.is_running.lock().await = true; + + let task_handle = tokio::spawn(async move { + Self::websocket_loop( + url, + config, + rx, + status, + queue, + is_running, + reconnect_attempts, + reconnect_delay_ms, + on_connected, + on_disconnected, + on_message, + on_binary, + on_error, + ) + .await; + }); + + *self.task_handle.lock().await = Some(task_handle); + } + + /// Main WebSocket loop + async fn websocket_loop( + url: String, + config: WebSocketConfig, + mut receiver: mpsc::Receiver, + status: Arc>, + queue: Arc>>, + is_running: Arc>, + reconnect_attempts: Arc>, + reconnect_delay_ms: Arc>, + on_connected: Option, + on_disconnected: Option, + on_message: Option, + on_binary: Option, + on_error: Option, + ) { + loop { + let should_run = *is_running.lock().await; + if !should_run { + break; + } + + match Self::connect_and_handle(&url, &config, &mut receiver, &queue, &on_connected, &on_message, &on_binary, &on_error).await { + Ok(_) => { + if config.debug_mode { + debug!("WebSocket connection closed normally"); + } + break; + } + Err(e) => { + if config.debug_mode { + warn!("WebSocket error: {:?}", e); + } + + if let Some(ref callback) = on_error { + callback(e.to_string()); + } + + if config.reconnect && *is_running.lock().await { + let delay = *reconnect_delay_ms.lock().await; + let attempts = *reconnect_attempts.lock().await + 1; + *reconnect_attempts.lock().await = attempts; + + if config.debug_mode { + debug!("Reconnecting in {}ms (attempt {})", delay, attempts); + } + + *status.lock().await = ConnectionStatus::Reconnecting; + tokio::time::sleep(Duration::from_millis(delay)).await; + + // Exponential backoff + let new_delay = std::cmp::min(delay * 2, config.max_reconnect_delay_ms); + *reconnect_delay_ms.lock().await = new_delay; + + continue; + } else { + break; + } + } + } + } + + *status.lock().await = ConnectionStatus::Disconnected; + if let Some(callback) = on_disconnected { + callback(); + } + } + + /// Connect and handle messages + async fn connect_and_handle( + url: &str, + config: &WebSocketConfig, + receiver: &mut mpsc::Receiver, + _queue: &Arc>>, + on_connected: &Option, + on_message: &Option, + on_binary: &Option, + on_error: &Option, + ) -> Result<(), WebSocketError> { + use http::header::{HeaderName, HeaderValue}; + use tokio_tungstenite::tungstenite::client::IntoClientRequest; + + // Build request with headers + let mut request = url.into_client_request()?; + + // Add client type header if specified + if let Some(ref client_type) = config.client_type { + request.headers_mut().insert( + HeaderName::from_static("clienttype"), + HeaderValue::from_str(client_type) + .map_err(|e| WebSocketError::HeaderError(e.to_string()))?, + ); + } + + // Add custom headers + for (key, value) in &config.custom_headers { + if let (Ok(name), Ok(val)) = ( + HeaderName::try_from(key.as_str()), + HeaderValue::from_str(value), + ) { + request.headers_mut().insert(name, val); + } + } + + // Connect with timeout + let ws_stream = tokio::time::timeout( + Duration::from_millis(config.connect_timeout_ms), + tokio_tungstenite::connect_async(request), + ) + .await + .map_err(|_| WebSocketError::ConnectionTimeout)? + .map_err(|e| WebSocketError::ConnectionFailed(e.to_string()))?; + + let (mut write, mut read) = ws_stream.0.split(); + + if config.debug_mode { + debug!("Connected to WebSocket server"); + } + + // Call on_connected callback + if let Some(ref callback) = on_connected { + callback(url.to_string()); + } + + // Start heartbeat task if enabled + let heartbeat_handle = if config.heartbeat_interval_ms > 0 { + let interval_duration = Duration::from_millis(config.heartbeat_interval_ms); + Some(tokio::spawn(async move { + let mut ticker = tokio::time::interval(interval_duration); + loop { + ticker.tick().await; + // Heartbeat will be sent via the receiver + } + })) + } else { + None + }; + + // Handle messages + loop { + tokio::select! { + // Incoming message from server + msg = read.next() => { + match msg { + Some(Ok(tokio_tungstenite::tungstenite::Message::Text(text))) => { + if config.debug_mode { + debug!("Received text: {}", text); + } + + // Parse and extract type + if let Ok(parsed) = serde_json::from_str::(&text) { + let msg_type = parsed.get("Type") + .and_then(|v| v.as_str()) + .unwrap_or("unknown") + .to_string(); + + if let Some(ref callback) = on_message { + callback(msg_type, parsed); + } + } else if let Some(ref callback) = on_message { + callback("raw".to_string(), serde_json::json!(text)); + } + } + Some(Ok(tokio_tungstenite::tungstenite::Message::Binary(data))) => { + if config.debug_mode { + debug!("Received binary: {} bytes", data.len()); + } + if let Some(ref callback) = on_binary { + callback(data); + } + } + Some(Ok(tokio_tungstenite::tungstenite::Message::Close(_))) => { + if config.debug_mode { + debug!("Server initiated close"); + } + break; + } + Some(Err(e)) => { + if config.debug_mode { + warn!("WebSocket read error: {}", e); + } + if let Some(ref callback) = on_error { + callback(e.to_string()); + } + break; + } + None => { + if config.debug_mode { + debug!("WebSocket stream ended"); + } + break; + } + _ => {} + } + } + + // Outgoing message + outgoing = receiver.recv() => { + match outgoing { + Some(OutgoingMessage::Text(text)) => { + if config.debug_mode { + debug!("Sending text: {}", text); + } + write.send(tokio_tungstenite::tungstenite::Message::Text(text)) + .await + .map_err(|e| WebSocketError::SendFailed(e.to_string()))?; + } + Some(OutgoingMessage::Binary(data)) => { + if config.debug_mode { + debug!("Sending binary: {} bytes", data.len()); + } + write.send(tokio_tungstenite::tungstenite::Message::Binary(data)) + .await + .map_err(|e| WebSocketError::SendFailed(e.to_string()))?; + } + Some(OutgoingMessage::Close) => { + if config.debug_mode { + debug!("Initiating close"); + } + write.close().await.ok(); + break; + } + None => { + break; + } + } + } + } + } + + // Wait for heartbeat handle + if let Some(handle) = heartbeat_handle { + handle.abort(); + } + + Ok(()) + } + + /// Disconnect from the server + pub async fn disconnect(&mut self) { + *self.is_running.lock().await = false; + + if let Some(sender) = self.sender.lock().await.take() { + let _ = sender.send(OutgoingMessage::Close).await; + } + + if let Some(handle) = self.task_handle.lock().await.take() { + handle.abort(); + } + + self.set_status(ConnectionStatus::Disconnected).await; + } + + // ==================== Message Sending ==================== + + /// Send a text message + pub async fn send(&self, msg_type: &str, data: Value) -> Result<(), WebSocketError> { + let message = WebSocketMessage::new(msg_type, data); + let json = serde_json::to_string(&message)?; + self.send_raw_text(json).await + } + + /// Send raw text + pub async fn send_raw_text(&self, text: String) -> Result<(), WebSocketError> { + let status = *self.status.lock().await; + + if status == ConnectionStatus::Connected { + if let Some(ref sender) = *self.sender.lock().await { + sender.send(OutgoingMessage::Text(text)) + .await + .map_err(|_| WebSocketError::NotConnected)?; + } + } else { + // Queue the message + let mut queue = self.message_queue.lock().await; + if queue.len() < self.config.max_queue_size { + if self.config.debug_mode { + debug!("Queueing message (queue size: {})", queue.len() + 1); + } + queue.push(OutgoingMessage::Text(text)); + } else { + if self.config.debug_mode { + warn!("Queue full, message dropped"); + } + return Err(WebSocketError::QueueFull); + } + } + Ok(()) + } + + /// Send binary data + pub async fn send_binary(&self, data: Vec) -> Result<(), WebSocketError> { + let status = *self.status.lock().await; + + if status == ConnectionStatus::Connected { + if let Some(ref sender) = *self.sender.lock().await { + sender.send(OutgoingMessage::Binary(data)) + .await + .map_err(|_| WebSocketError::NotConnected)?; + } + } else { + let mut queue = self.message_queue.lock().await; + if queue.len() < self.config.max_queue_size { + if self.config.debug_mode { + debug!("Queueing binary message"); + } + queue.push(OutgoingMessage::Binary(data)); + } else { + return Err(WebSocketError::QueueFull); + } + } + Ok(()) + } + + // ==================== Status & Queue ==================== + + /// Get current connection status + pub async fn get_status(&self) -> ConnectionStatus { + *self.status.lock().await + } + + /// Check if connected + pub async fn is_connected(&self) -> bool { + *self.status.lock().await == ConnectionStatus::Connected + } + + /// Get queue size + pub async fn get_queue_size(&self) -> usize { + self.message_queue.lock().await.len() + } + + /// Flush queued messages + pub async fn flush_queue(&self) -> Result<(), WebSocketError> { + let status = *self.status.lock().await; + if status != ConnectionStatus::Connected { + return Err(WebSocketError::NotConnected); + } + + let mut queue = self.message_queue.lock().await; + if let Some(ref sender) = *self.sender.lock().await { + while let Some(msg) = queue.pop() { + sender.send(msg) + .await + .map_err(|_| WebSocketError::NotConnected)?; + } + } + Ok(()) + } + + // ==================== Private Helpers ==================== + + #[allow(clippy::large_enum_variant)] + async fn set_status(&mut self, new_status: ConnectionStatus) { + let old_status = *self.status.lock().await; + if old_status != new_status { + *self.status.lock().await = new_status.clone(); + + // Reset reconnect state on successful connection + if new_status == ConnectionStatus::Connected { + *self.reconnect_attempts.lock().await = 0; + *self.reconnect_delay_ms.lock().await = self.config.reconnect_delay_ms; + } + + if let Some(ref callback) = self.on_status_changed { + callback(old_status, new_status); + } + } + } +} + +impl Drop for WebSocketClient { + fn drop(&mut self) { + // Attempt to disconnect synchronously + let _ = tokio::runtime::Handle::current().block_on(self.disconnect()); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_config_builder() { + let config = WebSocketConfig::new("ws://localhost:8080") + .with_client_type("Client") + .with_debug(true) + .with_reconnect(false); + + assert_eq!(config.ws_url, "ws://localhost:8080"); + assert_eq!(config.client_type, Some("Client".to_string())); + assert!(config.debug_mode); + assert!(!config.reconnect); + } + + #[tokio::test] + async fn test_client_creation() { + let config = WebSocketConfig::default(); + let client = WebSocketClient::new(config); + + let status = client.get_status().await; + assert_eq!(status, ConnectionStatus::Disconnected); + } +} diff --git a/Windows/Rust/CubeLib/src/websocket/config.rs b/Windows/Rust/CubeLib/src/websocket/config.rs new file mode 100644 index 0000000..e789944 --- /dev/null +++ b/Windows/Rust/CubeLib/src/websocket/config.rs @@ -0,0 +1,132 @@ +//! WebSocket configuration + +use serde::{Deserialize, Serialize}; + +/// WebSocket client configuration +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebSocketConfig { + /// WebSocket server URL (e.g., "ws://localhost:8087/ws") + pub ws_url: String, + + /// Auto-connect on initialization + #[serde(default = "default_true")] + pub auto_connect: bool, + + /// Auto-reconnect on disconnection + #[serde(default = "default_true")] + pub reconnect: bool, + + /// Maximum reconnect delay in milliseconds + #[serde(default = "default_max_reconnect_delay")] + pub max_reconnect_delay_ms: u64, + + /// Initial reconnect delay in milliseconds + #[serde(default = "default_reconnect_delay")] + pub reconnect_delay_ms: u64, + + /// Debug mode (enable logging) + #[serde(default)] + pub debug_mode: bool, + + /// Heartbeat interval in milliseconds (0 to disable) + #[serde(default = "default_heartbeat_interval")] + pub heartbeat_interval_ms: u64, + + /// Connection timeout in milliseconds + #[serde(default = "default_connect_timeout")] + pub connect_timeout_ms: u64, + + /// Maximum message queue size + #[serde(default = "default_max_queue_size")] + pub max_queue_size: usize, + + /// Custom HTTP headers + #[serde(default)] + pub custom_headers: Vec<(String, String)>, + + /// Client type for identification + #[serde(default)] + pub client_type: Option, +} + +fn default_true() -> bool { + true +} + +fn default_max_reconnect_delay() -> u64 { + 30000 +} + +fn default_reconnect_delay() -> u64 { + 1000 +} + +fn default_heartbeat_interval() -> u64 { + 30000 +} + +fn default_connect_timeout() -> u64 { + 10000 +} + +fn default_max_queue_size() -> usize { + 100 +} + +impl Default for WebSocketConfig { + fn default() -> Self { + Self { + ws_url: "ws://localhost:8087/ws".to_string(), + auto_connect: true, + reconnect: true, + max_reconnect_delay_ms: default_max_reconnect_delay(), + reconnect_delay_ms: default_reconnect_delay(), + debug_mode: false, + heartbeat_interval_ms: default_heartbeat_interval(), + connect_timeout_ms: default_connect_timeout(), + max_queue_size: default_max_queue_size(), + custom_headers: Vec::new(), + client_type: None, + } + } +} + +impl WebSocketConfig { + /// Create a new config with the given URL + pub fn new(ws_url: impl Into) -> Self { + Self { + ws_url: ws_url.into(), + ..Default::default() + } + } + + /// Set client type + pub fn with_client_type(mut self, client_type: impl Into) -> Self { + self.client_type = Some(client_type.into()); + self + } + + /// Set debug mode + pub fn with_debug(mut self, debug: bool) -> Self { + self.debug_mode = debug; + self + } + + /// Add a custom header + pub fn with_header(mut self, key: impl Into, value: impl Into) -> Self { + self.custom_headers.push((key.into(), value.into())); + self + } + + /// Enable auto-reconnect + pub fn with_reconnect(mut self, enabled: bool) -> Self { + self.reconnect = enabled; + self + } + + /// Set heartbeat interval (0 to disable) + pub fn with_heartbeat(mut self, interval_ms: u64) -> Self { + self.heartbeat_interval_ms = interval_ms; + self + } +} diff --git a/Windows/Rust/CubeLib/src/websocket/error.rs b/Windows/Rust/CubeLib/src/websocket/error.rs new file mode 100644 index 0000000..f94f920 --- /dev/null +++ b/Windows/Rust/CubeLib/src/websocket/error.rs @@ -0,0 +1,114 @@ +//! WebSocket error types + +use std::fmt; + +/// WebSocket-related errors +#[derive(Debug)] +pub enum WebSocketError { + /// Connection failed + ConnectionFailed(String), + + /// Connection timeout + ConnectionTimeout, + + /// Already connected + AlreadyConnected, + + /// Not connected + NotConnected, + + /// Send failed + SendFailed(String), + + /// JSON serialization error + SerializationError(String), + + /// JSON deserialization error + DeserializationError(String), + + /// Queue is full + QueueFull, + + /// Invalid URL + InvalidUrl(String), + + /// HTTP header error + HeaderError(String), + + /// IO error + IoError(String), +} + +impl fmt::Display for WebSocketError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + WebSocketError::ConnectionFailed(msg) => { + write!(f, "Connection failed: {}", msg) + } + WebSocketError::ConnectionTimeout => { + write!(f, "Connection timeout") + } + WebSocketError::AlreadyConnected => { + write!(f, "Already connected") + } + WebSocketError::NotConnected => { + write!(f, "Not connected") + } + WebSocketError::SendFailed(msg) => { + write!(f, "Send failed: {}", msg) + } + WebSocketError::SerializationError(msg) => { + write!(f, "Serialization error: {}", msg) + } + WebSocketError::DeserializationError(msg) => { + write!(f, "Deserialization error: {}", msg) + } + WebSocketError::QueueFull => { + write!(f, "Message queue is full") + } + WebSocketError::InvalidUrl(msg) => { + write!(f, "Invalid URL: {}", msg) + } + WebSocketError::HeaderError(msg) => { + write!(f, "Header error: {}", msg) + } + WebSocketError::IoError(msg) => { + write!(f, "IO error: {}", msg) + } + } + } +} + +impl std::error::Error for WebSocketError {} + +impl From for WebSocketError { + fn from(err: serde_json::Error) -> Self { + WebSocketError::SerializationError(err.to_string()) + } +} + +impl From for WebSocketError { + fn from(err: url::ParseError) -> Self { + WebSocketError::InvalidUrl(err.to_string()) + } +} + +impl From for WebSocketError { + fn from(err: tokio_tungstenite::tungstenite::Error) -> Self { + match err { + tokio_tungstenite::tungstenite::Error::ConnectionClosed => { + WebSocketError::NotConnected + } + tokio_tungstenite::tungstenite::Error::Io(err) => { + WebSocketError::IoError(err.to_string()) + } + _ => WebSocketError::ConnectionFailed(err.to_string()), + } + } +} + +impl From for WebSocketError { + fn from(err: http::Error) -> Self { + WebSocketError::HeaderError(err.to_string()) + } +} diff --git a/Windows/Rust/CubeLib/src/websocket/message.rs b/Windows/Rust/CubeLib/src/websocket/message.rs new file mode 100644 index 0000000..01b92eb --- /dev/null +++ b/Windows/Rust/CubeLib/src/websocket/message.rs @@ -0,0 +1,85 @@ +//! WebSocket message types + +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +/// Standard WebSocket message format (matches MasterSuite backend) +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebSocketMessage { + /// Message type + #[serde(rename = "Type")] + pub msg_type: String, + + /// Message data + #[serde(rename = "Data", default)] + pub data: Value, + + /// Optional message ID + #[serde(rename = "MessageId", skip_serializing_if = "Option::is_none")] + pub id: Option, +} + +impl WebSocketMessage { + /// Create a new message + pub fn new(msg_type: impl Into, data: Value) -> Self { + Self { + msg_type: msg_type.into(), + data, + id: None, + } + } + + /// Create with message ID + pub fn with_id(mut self, id: impl Into) -> Self { + self.id = Some(id.into()); + self + } + + /// Create a simple message with just type + pub fn simple(msg_type: impl Into) -> Self { + Self::new(msg_type, Value::Null) + } + + /// Create a message with JSON data + pub fn json(msg_type: impl Into, data: &T) -> Result { + let data = serde_json::to_value(data)?; + Ok(Self::new(msg_type, data)) + } +} + +/// Client type enumeration +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ClientType { + /// Regular client application + Client, + /// Web interface + Web, + /// Updater program + Updater, +} + +impl ClientType { + /// Parse from header value + pub fn from_header(value: &str) -> Self { + match value { + "App" | "Client" => ClientType::Client, + "Updater" => ClientType::Updater, + _ => ClientType::Web, + } + } + + /// Convert to header value + pub fn as_header(&self) -> &'static str { + match self { + ClientType::Client => "App", + ClientType::Web => "Web", + ClientType::Updater => "Updater", + } + } +} + +impl std::fmt::Display for ClientType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.as_header()) + } +} diff --git a/Windows/Rust/CubeLib/src/websocket/mod.rs b/Windows/Rust/CubeLib/src/websocket/mod.rs new file mode 100644 index 0000000..8752350 --- /dev/null +++ b/Windows/Rust/CubeLib/src/websocket/mod.rs @@ -0,0 +1,18 @@ +//! WebSocket module +//! +//! Provides WebSocket client and server implementations with support for: +//! - Custom headers (ClientType) +//! - Auto-reconnect +//! - Heartbeat/ping-pong +//! - Message queuing +//! - Event callbacks + +mod client; +mod config; +mod message; +mod error; + +pub use client::WebSocketClient; +pub use config::WebSocketConfig; +pub use message::{WebSocketMessage, ClientType}; +pub use error::WebSocketError; diff --git a/Windows/Rust/CubeLib/target/.rustc_info.json b/Windows/Rust/CubeLib/target/.rustc_info.json new file mode 100644 index 0000000..d391746 --- /dev/null +++ b/Windows/Rust/CubeLib/target/.rustc_info.json @@ -0,0 +1 @@ +{"rustc_fingerprint":17656983458485528297,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.94.1 (e408947bf 2026-03-25)\nbinary: rustc\ncommit-hash: e408947bfd200af42db322daf0fadfe7e26d3bd1\ncommit-date: 2026-03-25\nhost: x86_64-pc-windows-msvc\nrelease: 1.94.1\nLLVM version: 21.1.8\n","stderr":""},"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\xyzqm\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"pc\"\nwindows\n","stderr":""}},"successes":{}} \ No newline at end of file diff --git a/Windows/Rust/CubeLib/target/CACHEDIR.TAG b/Windows/Rust/CubeLib/target/CACHEDIR.TAG new file mode 100644 index 0000000..20d7c31 --- /dev/null +++ b/Windows/Rust/CubeLib/target/CACHEDIR.TAG @@ -0,0 +1,3 @@ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by cargo. +# For information about cache directory tags see https://bford.info/cachedir/