diff --git a/Cargo.lock b/Cargo.lock index cd2cdd6..ef61a65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,219 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-http" +version = "3.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7926860314cbe2fb5d1f13731e387ab43bd32bca224e82e6e2db85de0a3dba49" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "base64", + "bitflags", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "foldhash", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +dependencies = [ + "bytestring", + "cfg-if", + "http", + "regex", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2 0.5.10", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1654a77ba142e37f049637a3e5685f864514af11fcbc51cb51eb6596afe5b8d6" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "foldhash", + "futures-core", + "futures-util", + "impl-more", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2 0.6.2", + "time", + "tracing", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -58,12 +271,63 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[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 = "brotli" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "bytestring" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289" +dependencies = [ + "bytes", +] + [[package]] name = "cassowary" version = "0.3.0" @@ -79,6 +343,18 @@ dependencies = [ "rustversion", ] +[[package]] +name = "cc" +version = "1.2.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -145,6 +421,44 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossterm" version = "0.28.1" @@ -170,6 +484,16 @@ dependencies = [ "winapi", ] +[[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 = "darling" version = "0.23.0" @@ -204,12 +528,74 @@ dependencies = [ "syn", ] +[[package]] +name = "deranged" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", + "unicode-xid", +] + +[[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 = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -226,12 +612,114 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[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.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[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.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -243,18 +731,165 @@ 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 = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[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 = "impl-more" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + [[package]] name = "indoc" version = "2.0.7" @@ -298,6 +933,22 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom", + "libc", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + [[package]] name = "libc" version = "0.2.181" @@ -310,6 +961,29 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + [[package]] name = "lock_api" version = "0.4.14" @@ -331,7 +1005,29 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown", + "hashbrown 0.15.5", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", ] [[package]] @@ -346,6 +1042,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + [[package]] name = "once_cell_polyfill" version = "1.70.2" @@ -381,6 +1089,54 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[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.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -399,6 +1155,41 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom", +] + [[package]] name = "ratatui" version = "0.29.0" @@ -429,6 +1220,50 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[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-lite" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.44" @@ -460,6 +1295,84 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[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 = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[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 = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook" version = "0.3.18" @@ -491,6 +1404,18 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[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" @@ -501,11 +1426,39 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" name = "soccercloud" version = "0.1.0" dependencies = [ + "actix-web", "clap", "crossterm", "ratatui", + "serde", ] +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "static_assertions" version = "1.1.0" @@ -551,6 +1504,125 @@ dependencies = [ "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 = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "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", +] + +[[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.23" @@ -586,18 +1658,57 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +[[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 = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[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 = "winapi" version = "0.3.9" @@ -626,13 +1737,31 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -650,14 +1779,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -666,44 +1812,235 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4de98dfa5d5b7fef4ee834d0073d560c9ca7b6c46a71d058c48db7960f8cfaf7" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 0b14ec8..881ccd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" ratatui = "0.29" crossterm = "0.28" clap = { version = "4.5", features = ["derive"] } +actix-web = "4.11" +serde = { version = "1.0", features = ["derive"] } [profile.release] opt-level = 3 diff --git a/DEVLOG.md b/DEVLOG.md index 7f5a802..2cb27c4 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -78,3 +78,31 @@ - Added modal scrolling controls (`j/k` or up/down) and close controls (`Esc` or `q`). - Simplified detail view to focus on scoreboard, logs, and instance info. - Added detail-panel hint bar to direct users to the new dedicated modals. + +## 2026-02-10 - National team expansion + +### Scope completed +- Expanded team database to include 50+ national teams in addition to existing clubs. +- Added national-team flag mappings, including `PRC China`. +- Added tactic/formation profile mappings for the new national teams. +- Verified with `list` and deterministic `quick` simulation using national teams. + +## 2026-02-11 - Web mode with Actix and Rust-backed frontend + +### Scope completed +- Added `--web` launch mode to start an Actix server at `127.0.0.1:9009`. +- Implemented web APIs for team listing and simulation lifecycle: + - create/list/detail/start/clone/delete/export CSV +- Reused Rust simulation engine and instance lifecycle for backend execution. +- Rebuilt `index.html` and `data.js` as a modern web dashboard UI backed by Rust APIs. +- Removed legacy client-side simulation engine from the browser path. + +## 2026-02-11 - Open listen option for web mode + +### Scope completed +- Added `--listen-open` CLI argument for web mode. +- Enforced `--listen-open` usage only when `--web` is present. +- Updated web server bind address behavior: + - default: `127.0.0.1:9009` + - open listen: `0.0.0.0:9009` +- Updated startup logs and README usage examples for LAN-accessible mode. diff --git a/README.md b/README.md index 7eb6e09..bbc4179 100644 --- a/README.md +++ b/README.md @@ -1,126 +1,198 @@ -# โšฝ SoccerCloud โ€” Cloudified Soccer Simulation Environment +# SoccerCloud CLI (Rust) -**Live Demo:** [https://mentalnet.xyz/soccercloud/](https://mentalnet.xyz/soccercloud/) -**Author:** [markmental / MentalNet.xyz](https://mentalnet.xyz) -**License:** MIT +

+ SoccerCloud Logo +

---- +Terminal-native rebuild of MentalNet SoccerCloud with a cloud-dashboard feel. -## ๐Ÿง  Overview +## Overview -**SoccerCloud** is a browser-based soccer simulator that reimagines match simulations through the aesthetic and structure of a **cloud orchestration dashboard** โ€” think *OpenStack meets Football Manager*. +This project is a Rust TUI/CLI soccer simulator with: -Each match, league, or knockout bracket behaves like a **โ€œvirtual instanceโ€**, complete with lifecycle controls: -- **Create / Start / View / Delete / Clone / Export** -- Real-time logs, xG data, formations, and tactical analytics -- **Dynamic UI** styled like a cloud console with per-match telemetry +- Single Match, 4-Team League, and 4-Team Knockout modes +- Live match logs, scoreboard, and instance lifecycle controls +- Seeded deterministic runs (`--seed`) for reproducible results +- CSV export for single, league, and knockout outputs +- Expanded team pool (clubs + 50+ national teams, including `PRC China`) -SoccerCloud is written entirely in **HTML, CSS, and vanilla JavaScript**, with no external backend dependencies. It runs fully client-side and is suitable for static hosting. +## Requirements ---- +- Rust toolchain (stable) +- Cargo +- A terminal that supports UTF-8 and colors -## ๐ŸŒ Live Deployment - -> [https://mentalnet.xyz/soccercloud/](https://mentalnet.xyz/soccercloud/) - -Hosted on **MentalNet.xyz**, the current deployment showcases all features including: -- Match instance dashboard -- 4-team League and Knockout modes -- CSV export of results and tables -- Auto-team picker with J-League and European clubs -- Cloud-inspired modal configuration UI - ---- - -## ๐Ÿ—๏ธ Features - -| Category | Description | -|-----------|-------------| -| **Simulation Types** | Single Match, 4-Team League, 4-Team Knockout | -| **Team Database** | Includes J-League + top European clubs with realistic formations/tactics | -| **UI Design** | Styled like a lightweight OpenStack/Proxmox console | -| **Export Options** | Download match or league data as CSV | -| **Logging & Recaps** | Live xG updates, goal commentary, and tactical analysis | -| **Client-Only** | Runs directly in browser โ€” no backend needed | - ---- - -## ๐Ÿ—‚๏ธ Project Structure - -``` - -soccercloud/ -โ”œโ”€โ”€ index.html # Main web dashboard and simulation logic -โ”œโ”€โ”€ data.js # Team definitions, flags, formations, and tactics -โ””โ”€โ”€ assets/ # (Optional) icons, logos, or future expansion files - -```` - ---- - -## ๐Ÿš€ Getting Started (Local) - -You can run SoccerCloud locally with **no build process** โ€” just open it in a browser. - -### Option 1: Double-click -```bash -open index.html -```` - -### Option 2: Local dev server +Install Rust (if needed): ```bash -python3 -m http.server 8080 +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` -Then visit: -๐Ÿ‘‰ `http://localhost:8080` +## Setup ---- +Clone and build: -## ๐Ÿงฉ Technical Notes +```bash +git clone https://mentalnet.xyz/forgejo/markmental/soccercloud-rust.git +cd soccercloud-cli +cargo check +``` -* Written in **vanilla JavaScript** for speed and transparency. -* Each simulation instance is handled via a `SimulationInstance` class. -* Data persistence is session-based; future versions may support saving instance states. -* CSS uses retro **UnifrakturCook + Press Start 2P** fonts for a distinct MentalNet look. +Run in debug mode: ---- +```bash +cargo run +``` -## ๐Ÿ–ฅ๏ธ Upcoming: CLI Edition +Build optimized binary: -> โšก **tuxsoccercloud** *(Coming soon!)* +```bash +cargo build --release +./target/release/soccercloud +``` -A simplified **terminal version** of the simulator is in development โ€” ideal for users who prefer a command-line workflow or want to integrate match simulations into scripts or data pipelines. +## CLI Usage -Planned features: +Default (interactive TUI): -* Text-only match recaps and league tables -* Randomized or argument-based team selection -* Fully offline operation +```bash +soccercloud +``` ---- +or with Cargo: -## ๐Ÿค Contributing +```bash +cargo run -- +``` -Pull requests are welcome (when I get signups up)! -To contribute: +Use a global seed for reproducibility: -1. Fork this repository -2. Make your edits in a feature branch -3. Submit a pull request with a clear description +```bash +cargo run -- --seed 42 +``` ---- +### Web mode (Actix) -## ๐Ÿ’ก Credits +Launch the web UI on port `9009`: -* Built and designed by **markmental** -* Hosted under **MentalNet.xyz** -* Inspired by *OpenStack Horizon* dashboards and *Football Manager*-style simulations -* Font assets via [Google Fonts](https://fonts.google.com) -* Icons via [Font Awesome](https://fontawesome.com) +```bash +cargo run -- --web +``` ---- +Then open: -### โšฝ *"Deploy your next match like a VM โ€” welcome to SoccerCloud."* +```text +http://127.0.0.1:9009 +``` +Open web mode on all network interfaces (`0.0.0.0:9009`) so other machines can access it: + +```bash +cargo run -- --web --listen-open +``` + +Notes: +- The web frontend (`index.html` + `data.js`) now uses Rust backend APIs. +- Simulation logic runs server-side in Rust (shared with CLI/TUI engine). +- `--listen-open` is only valid with `--web` and should be used on trusted networks. + +### Quick match (headless) + +```bash +cargo run -- quick --home "Arsenal" --away "Real Madrid" --seed 42 +``` + +CPU auto-fill for missing team(s): + +```bash +cargo run -- quick --home "England" --seed 42 +cargo run -- quick --seed 42 +``` + +### List teams + +```bash +cargo run -- list +``` + +### Export CSV + +Single: + +```bash +cargo run -- export --mode single --team "Arsenal" --team "PRC China" --out match.csv --seed 42 +``` + +League: + +```bash +cargo run -- export --mode league4 --team "England" --team "Brazil" --team "Japan" --team "Germany" --out league.csv --seed 42 +``` + +Knockout: + +```bash +cargo run -- export --mode knockout4 --team "France" --team "Argentina" --team "Morocco" --team "PRC China" --out knockout.csv --seed 42 +``` + +## TUI Controls + +Global: + +- `n` create Single instance +- `l` create League4 instance +- `o` create Knockout4 instance +- `s` start selected instance +- `c` clone selected instance +- `d` delete selected instance +- `e` export selected instance CSV +- `v` or `Enter` toggle dashboard/detail +- `j/k` or `Up/Down` navigate instances +- `1/2/4/0` speed control (1x/2x/4x/instant) +- `q` quit + +Create modal: + +- `m` set selected slot to manual team +- `p` set selected slot to CPU auto-fill +- `[` / `]` or `Left/Right` cycle manual team +- `Enter` create +- `Esc` cancel + +Readable fullscreen data panels: + +- `t` stats modal +- `g` standings/bracket modal +- `h` history modal +- `j/k` or `Up/Down` scroll inside modal +- `Esc` or `q` close modal + +## Project Structure + +```text +src/ +โ”œโ”€โ”€ main.rs # CLI entrypoint and commands +โ”œโ”€โ”€ web.rs # Actix web server + JSON APIs +โ”œโ”€โ”€ app.rs # App state and event loop +โ”œโ”€โ”€ data.rs # Teams, flags, tactics, profiles +โ”œโ”€โ”€ sim.rs # Match/league/knockout simulation engine +โ”œโ”€โ”€ instance.rs # Simulation instance lifecycle and state +โ”œโ”€โ”€ export.rs # CSV export +โ”œโ”€โ”€ utils.rs # RNG + helper utilities +โ””โ”€โ”€ ui/ + โ”œโ”€โ”€ mod.rs + โ”œโ”€โ”€ dashboard.rs + โ”œโ”€โ”€ detail.rs + โ”œโ”€โ”€ modal.rs + โ””โ”€โ”€ widgets.rs +``` + +## Notes + +- Dependency policy is intentionally strict (minimal crates). +- Team data is embedded in the binary (no external runtime data files). +- Use `--seed` for deterministic comparisons and debugging. + +## License + +MIT diff --git a/data.js b/data.js index 9e2fd71..e83f1d4 100644 --- a/data.js +++ b/data.js @@ -1,80 +1,444 @@ -// ======================================================= -// SoccerCloud Simulator Data File -// Contains all team, flag, and profile information. -// ======================================================= - -const teams = [ - // J-League - "Kashima Antlers","Urawa Red Diamonds","Gamba Osaka","Cerezo Osaka","Kawasaki Frontale", - "Yokohama F. Marinos","Nagoya Grampus","Shimizu S-Pulse","Sanfrecce Hiroshima","Consadole Sapporo", - "Ventforet Kofu","Tokyo Verdy","JEF United Chiba", - // Euro clubs - "Arsenal","FC Barcelona","Real Madrid","Manchester City","Manchester United","Liverpool", - "Bayern Munich","Borussia Dortmund","Paris Saint-Germain","Juventus","Inter","AC Milan", - "Ajax","Benfica","Porto","Celtic" -]; - -const TEAM_FLAGS = { - // Japan - "Kashima Antlers":"๐Ÿ‡ฏ๐Ÿ‡ต","Urawa Red Diamonds":"๐Ÿ‡ฏ๐Ÿ‡ต","Gamba Osaka":"๐Ÿ‡ฏ๐Ÿ‡ต","Cerezo Osaka":"๐Ÿ‡ฏ๐Ÿ‡ต","Kawasaki Frontale":"๐Ÿ‡ฏ๐Ÿ‡ต", - "Yokohama F. Marinos":"๐Ÿ‡ฏ๐Ÿ‡ต","Nagoya Grampus":"๐Ÿ‡ฏ๐Ÿ‡ต","Shimizu S-Pulse":"๐Ÿ‡ฏ๐Ÿ‡ต","Sanfrecce Hiroshima":"๐Ÿ‡ฏ๐Ÿ‡ต","Consadole Sapporo":"๐Ÿ‡ฏ๐Ÿ‡ต", - "Ventforet Kofu":"๐Ÿ‡ฏ๐Ÿ‡ต","Tokyo Verdy":"๐Ÿ‡ฏ๐Ÿ‡ต", "JEF United Chiba":"๐Ÿ‡ฏ๐Ÿ‡ต", - // UK - "Arsenal":"๐Ÿ‡ฌ๐Ÿ‡ง","Manchester City":"๐Ÿ‡ฌ๐Ÿ‡ง","Manchester United":"๐Ÿ‡ฌ๐Ÿ‡ง","Liverpool":"๐Ÿ‡ฌ๐Ÿ‡ง","Celtic":"๐Ÿ‡ฌ๐Ÿ‡ง", - // Spain - "FC Barcelona":"๐Ÿ‡ช๐Ÿ‡ธ","Real Madrid":"๐Ÿ‡ช๐Ÿ‡ธ", - // Germany - "Bayern Munich":"๐Ÿ‡ฉ๐Ÿ‡ช","Borussia Dortmund":"๐Ÿ‡ฉ๐Ÿ‡ช", - // France - "Paris Saint-Germain":"๐Ÿ‡ซ๐Ÿ‡ท", - // Italy - "Juventus":"๐Ÿ‡ฎ๐Ÿ‡น","Inter":"๐Ÿ‡ฎ๐Ÿ‡น","AC Milan":"๐Ÿ‡ฎ๐Ÿ‡น", - // Netherlands - "Ajax":"๐Ÿ‡ณ๐Ÿ‡ฑ", - // Portugal - "Benfica":"๐Ÿ‡ต๐Ÿ‡น","Porto":"๐Ÿ‡ต๐Ÿ‡น" +const state = { + teams: [], + simulations: [], + selectedDetailId: null, + pollHandle: null, }; -const FORMATIONS = ["4-4-2","4-3-3","4-2-3-1","3-5-2","5-4-1"]; - -const TACTICS = { - counter: { label:"Counter", attackBias:1.10, goalMult:1.08, fastBreak:0.25, foulMult:1.00, blockMult:1.00, pressMult:0.95 }, - possession: { label:"Possession", attackBias:1.00, goalMult:0.95, fastBreak:0.10, foulMult:0.90, blockMult:1.00, pressMult:0.90 }, - high_press: { label:"High Press", attackBias:1.15, goalMult:1.00, fastBreak:0.20, foulMult:1.20, blockMult:0.95, pressMult:1.20 }, - low_block: { label:"Low Block", attackBias:0.92, goalMult:0.92, fastBreak:0.12, foulMult:0.95, blockMult:1.15, pressMult:0.85 }, +const $ = (id) => document.getElementById(id); +const THEME_STORAGE_KEY = "soccercloud.web.theme"; +const THEMES = { + "default-light": "Default (Light)", + dark: "Dark", + matcha: "Matcha", + "black-mesa": "Black Mesa", }; -const TEAM_PROFILES = { - // Europe - "Arsenal": { formation:"4-3-3", tactic:"possession" }, - "FC Barcelona": { formation:"4-3-3", tactic:"possession" }, - "Real Madrid": { formation:"4-3-3", tactic:"counter" }, - "Manchester City": { formation:"4-3-3", tactic:"possession" }, - "Manchester United": { formation:"4-2-3-1", tactic:"high_press" }, - "Liverpool": { formation:"4-3-3", tactic:"high_press" }, - "Bayern Munich": { formation:"4-2-3-1", tactic:"high_press" }, - "Borussia Dortmund": { formation:"4-2-3-1", tactic:"high_press" }, - "Paris Saint-Germain": { formation:"4-3-3", tactic:"possession" }, - "Juventus": { formation:"3-5-2", tactic:"low_block" }, - "Inter": { formation:"3-5-2", tactic:"low_block" }, - "AC Milan": { formation:"4-2-3-1", tactic:"possession" }, - "Ajax": { formation:"4-3-3", tactic:"possession" }, - "Benfica": { formation:"4-2-3-1", tactic:"possession" }, - "Porto": { formation:"4-4-2", tactic:"counter" }, - "Celtic": { formation:"4-3-3", tactic:"possession" }, - // J-League (generic lean) - "Kawasaki Frontale": { formation:"4-3-3", tactic:"possession" }, - "Yokohama F. Marinos": { formation:"4-3-3", tactic:"high_press" }, - "Kashima Antlers": { formation:"4-4-2", tactic:"counter" }, - "Urawa Red Diamonds": { formation:"4-2-3-1", tactic:"possession" }, - "Gamba Osaka": { formation:"4-4-2", tactic:"counter" }, - "Cerezo Osaka": { formation:"4-4-2", tactic:"counter" }, - "Nagoya Grampus": { formation:"4-2-3-1", tactic:"low_block" }, - "Sanfrecce Hiroshima": { formation:"3-5-2", tactic:"possession" }, - "Consadole Sapporo": { formation:"3-5-2", tactic:"high_press" }, - "Shimizu S-Pulse": { formation:"4-4-2", tactic:"counter" }, - "Ventforet Kofu": { formation:"4-4-2", tactic:"counter" }, - "Tokyo Verdy": { formation:"4-3-3", tactic:"possession" }, - "JEF United Chiba": { formation:"4-3-3", tactic:"counter" } -}; +function isThemeAllowed(theme) { + return Object.prototype.hasOwnProperty.call(THEMES, theme); +} +function getSavedTheme() { + try { + const saved = localStorage.getItem(THEME_STORAGE_KEY); + if (saved && isThemeAllowed(saved)) { + return saved; + } + } catch (_) {} + return "default-light"; +} + +function applyTheme(theme) { + const normalized = isThemeAllowed(theme) ? theme : "default-light"; + document.documentElement.dataset.theme = normalized; + const select = $("themeSelect"); + if (select && select.value !== normalized) { + select.value = normalized; + } + try { + localStorage.setItem(THEME_STORAGE_KEY, normalized); + } catch (_) {} +} + +function initThemeControls() { + const select = $("themeSelect"); + if (!select) return; + const initial = getSavedTheme(); + applyTheme(initial); + select.addEventListener("change", () => { + applyTheme(select.value); + setStatus(`Theme: ${THEMES[select.value] || "Default (Light)"}`); + }); +} + +function setStatus(message) { + $("statusText").textContent = message; +} + +async function request(path, options = {}) { + const response = await fetch(path, options); + if (!response.ok) { + let msg = `${response.status} ${response.statusText}`; + try { + const body = await response.json(); + if (body && body.error) { + msg = body.error; + } + } catch (_) {} + throw new Error(msg); + } + + if (response.status === 204) { + return null; + } + + const contentType = response.headers.get("content-type") || ""; + if (contentType.includes("application/json")) { + return response.json(); + } + return response.text(); +} + +function openModal(modalId) { + const modal = $(modalId); + if (!modal) return; + modal.classList.add("open"); + modal.setAttribute("aria-hidden", "false"); +} + +function closeModal(modalId) { + const modal = $(modalId); + if (!modal) return; + modal.classList.remove("open"); + modal.setAttribute("aria-hidden", "true"); +} + +function getModeTeamCount(mode) { + return mode === "single" ? 2 : 4; +} + +function renderTeamSelectors() { + const wrap = $("teamSelectWrap"); + const mode = $("modeSelect").value; + const required = getModeTeamCount(mode); + const autoFill = $("autoFill").checked; + + $("teamCount").innerHTML = ``; + + if (state.teams.length === 0) { + wrap.innerHTML = "

Loading teams...

"; + return; + } + + const options = state.teams + .map((team) => ``) + .join(""); + + const fields = []; + for (let i = 0; i < required; i++) { + fields.push(` + + `); + } + + wrap.innerHTML = fields.join(""); +} + +function getCreatePayload() { + const mode = $("modeSelect").value; + const autoFill = $("autoFill").checked; + const required = getModeTeamCount(mode); + + if (autoFill) { + return { mode, auto_fill: true }; + } + + const picks = [...$("teamSelectWrap").querySelectorAll("select")].map((s) => s.value); + const uniqueCount = new Set(picks).size; + if (picks.length !== required || uniqueCount !== picks.length) { + throw new Error("Please select unique teams for this mode"); + } + + return { mode, auto_fill: false, teams: picks }; +} + +function cardActions(sim) { + const common = ` + + + + `; + + if (sim.status === "pending") { + return ` + + ${common} + `; + } + + if (sim.status === "completed") { + return ` + + ${common} + `; + } + + return common; +} + +function createCardElement(sim) { + const article = document.createElement("article"); + article.className = "card"; + article.dataset.id = String(sim.id); + article.innerHTML = ` +
+
+

+

+
+ +
+

Progress:

+

+

Outcome:

+
+ `; + return article; +} + +function setTextIfChanged(element, nextValue) { + if (!element) return; + if (element.textContent !== nextValue) { + element.textContent = nextValue; + } +} + +function updateCardElement(card, sim) { + setTextIfChanged(card.querySelector('[data-role="title"]'), sim.title); + setTextIfChanged(card.querySelector('[data-role="idline"]'), `sim-${sim.id} | ${sim.mode} | seed=${sim.seed}`); + setTextIfChanged(card.querySelector('[data-role="progress"]'), sim.progress); + setTextIfChanged(card.querySelector('[data-role="scoreboard"]'), sim.scoreboard); + setTextIfChanged(card.querySelector('[data-role="outcome"]'), sim.outcome); + + const pill = card.querySelector('[data-role="pill"]'); + const nextPillClass = `pill ${sim.status}`; + if (pill.className !== nextPillClass) { + pill.className = nextPillClass; + } + setTextIfChanged(pill, sim.status); + + if (card.dataset.status !== sim.status) { + card.dataset.status = sim.status; + card.querySelector('[data-role="actions"]').innerHTML = cardActions(sim); + } +} + +function renderDashboard() { + const root = $("dashboard"); + if (!root) return; + + if (state.simulations.length === 0) { + for (const card of root.querySelectorAll("article.card")) { + card.remove(); + } + if (!$("dashboardEmpty")) { + root.innerHTML = `
No simulation instances yet. Create one to start the web simulation flow.
`; + } + return; + } + + const emptyNode = $("dashboardEmpty"); + if (emptyNode) { + emptyNode.remove(); + } + + const existingCards = new Map( + [...root.querySelectorAll("article.card")].map((card) => [Number(card.dataset.id), card]), + ); + + const activeIds = new Set(state.simulations.map((sim) => sim.id)); + + for (let index = 0; index < state.simulations.length; index += 1) { + const sim = state.simulations[index]; + let card = existingCards.get(sim.id); + if (!card) { + card = createCardElement(sim); + root.appendChild(card); + } + + updateCardElement(card, sim); + + if (root.children[index] !== card) { + root.insertBefore(card, root.children[index] || null); + } + } + + for (const [id, card] of existingCards.entries()) { + if (!activeIds.has(id)) { + card.remove(); + } + } +} + +function renderStats() { + const total = state.simulations.length; + const running = state.simulations.filter((s) => s.status === "running").length; + const completed = state.simulations.filter((s) => s.status === "completed").length; + $("statInstances").textContent = String(total); + $("statRunning").textContent = String(running); + $("statCompleted").textContent = String(completed); + $("statTeams").textContent = String(state.teams.length); +} + +async function refreshSimulations() { + state.simulations = await request("/api/simulations"); + renderStats(); + renderDashboard(); + + if (state.selectedDetailId !== null) { + const exists = state.simulations.some((s) => s.id === state.selectedDetailId); + if (!exists) { + state.selectedDetailId = null; + closeModal("detailModal"); + return; + } + await loadDetail(state.selectedDetailId); + } +} + +function textOrPlaceholder(lines, fallback) { + if (!Array.isArray(lines) || lines.length === 0) { + return fallback; + } + return lines.join("\n"); +} + +async function loadDetail(id) { + const detail = await request(`/api/simulations/${id}`); + state.selectedDetailId = id; + + $("detailTitle").textContent = `sim-${detail.id} - ${detail.title}`; + $("detailScoreboard").textContent = `${detail.scoreboard} | ${detail.outcome}`; + $("detailLogs").textContent = textOrPlaceholder(detail.logs, "No events yet."); + $("detailStats").textContent = textOrPlaceholder(detail.stats_lines, "No stats available yet."); + $("detailCompetition").textContent = textOrPlaceholder(detail.competition_lines, "No standings/bracket available yet."); + $("detailHistory").textContent = textOrPlaceholder(detail.history_lines, "No history recorded yet."); +} + +async function createSimulation() { + try { + const payload = getCreatePayload(); + const created = await request("/api/simulations", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + closeModal("createModal"); + setStatus(`Created sim-${created.id}`); + await refreshSimulations(); + } catch (error) { + setStatus(`Create failed: ${error.message}`); + } +} + +async function startSimulation(id) { + try { + await request(`/api/simulations/${id}/start`, { method: "POST" }); + setStatus(`Started sim-${id}`); + await refreshSimulations(); + } catch (error) { + setStatus(`Start failed: ${error.message}`); + } +} + +async function cloneSimulation(id) { + try { + const created = await request(`/api/simulations/${id}/clone`, { method: "POST" }); + setStatus(`Cloned sim-${id} to sim-${created.id}`); + await refreshSimulations(); + } catch (error) { + setStatus(`Clone failed: ${error.message}`); + } +} + +async function deleteSimulation(id) { + try { + await request(`/api/simulations/${id}`, { method: "DELETE" }); + if (state.selectedDetailId === id) { + state.selectedDetailId = null; + closeModal("detailModal"); + } + setStatus(`Deleted sim-${id}`); + await refreshSimulations(); + } catch (error) { + setStatus(`Delete failed: ${error.message}`); + } +} + +function exportSimulation(id) { + window.location.href = `/api/simulations/${id}/export.csv`; + setStatus(`Exporting sim-${id} CSV...`); +} + +function bindEvents() { + $("openCreateBtn").addEventListener("click", () => openModal("createModal")); + $("createBtn").addEventListener("click", createSimulation); + $("modeSelect").addEventListener("change", renderTeamSelectors); + $("autoFill").addEventListener("change", renderTeamSelectors); + + document.querySelectorAll("[data-close]").forEach((button) => { + button.addEventListener("click", () => { + closeModal(button.dataset.close); + if (button.dataset.close === "detailModal") { + state.selectedDetailId = null; + } + }); + }); + + ["createModal", "detailModal"].forEach((id) => { + const modal = $(id); + modal.addEventListener("click", (event) => { + if (event.target === modal) { + closeModal(id); + if (id === "detailModal") { + state.selectedDetailId = null; + } + } + }); + }); + + $("dashboard").addEventListener("click", async (event) => { + const button = event.target.closest("button[data-action]"); + if (!button) return; + + const id = Number(button.dataset.id); + const action = button.dataset.action; + + if (action === "start") return startSimulation(id); + if (action === "clone") return cloneSimulation(id); + if (action === "delete") return deleteSimulation(id); + if (action === "export") return exportSimulation(id); + if (action === "view") { + try { + await loadDetail(id); + openModal("detailModal"); + } catch (error) { + setStatus(`Failed to load detail: ${error.message}`); + } + } + }); +} + +async function boot() { + initThemeControls(); + bindEvents(); + try { + setStatus("Loading teams and simulations..."); + state.teams = await request("/api/teams"); + renderTeamSelectors(); + await refreshSimulations(); + setStatus("Connected to SoccerCloud backend on port 9009."); + } catch (error) { + setStatus(`Startup failed: ${error.message}`); + return; + } + + if (state.pollHandle) { + clearInterval(state.pollHandle); + } + + state.pollHandle = setInterval(async () => { + try { + await refreshSimulations(); + } catch (error) { + setStatus(`Polling issue: ${error.message}`); + } + }, 500); +} + +boot(); diff --git a/index.html b/index.html index 17a6ede..0add5b9 100644 --- a/index.html +++ b/index.html @@ -3,648 +3,724 @@ - MentalNet.xyz | SoccerCloud Dashboard - - - + + SoccerCloud Web + + + + + body { + margin: 0; + min-height: 100vh; + font-family: "Space Grotesk", "Segoe UI", sans-serif; + color: var(--text); + background: + radial-gradient(1200px 640px at -8% -12%, var(--bg-radial-1), transparent 58%), + radial-gradient(980px 680px at 110% -18%, var(--bg-radial-2), transparent 55%), + linear-gradient(180deg, var(--bg-start) 0%, var(--bg-end) 100%); + } + + .app { + max-width: 1200px; + margin: 0 auto; + padding: 28px 18px 42px; + } + + .hero { + display: grid; + grid-template-columns: 1.2fr 0.8fr; + gap: 18px; + margin-bottom: 18px; + } + + .hero-card { + background: linear-gradient(145deg, var(--hero-grad-start), var(--hero-grad-end)); + border: 1px solid var(--hero-border); + border-radius: 18px; + padding: 20px; + box-shadow: var(--shadow); + animation: rise 420ms ease both; + } + + .eyebrow { + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--brand); + margin: 0 0 8px; + font-weight: 700; + } + + .hero-logo { + width: 80px; + height: auto; + display: block; + border-radius: 14px; + margin: 0 0 12px; + border: 1px solid var(--line); + box-shadow: 0 8px 22px rgba(15, 23, 42, 0.16); + } + + h1 { + font-family: "Sora", "Space Grotesk", sans-serif; + margin: 0; + font-size: clamp(28px, 5vw, 42px); + line-height: 1.05; + letter-spacing: -0.02em; + } + + .lead { + margin: 12px 0 0; + color: var(--muted); + font-size: 15px; + max-width: 56ch; + } + + .stat-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; + } + + .stat { + border: 1px solid var(--line); + background: var(--surface-soft); + border-radius: 12px; + padding: 10px 12px; + } + + .stat .label { + color: var(--muted); + font-size: 12px; + margin-bottom: 6px; + } + + .stat .value { + font-family: "Sora", "Space Grotesk", sans-serif; + font-size: 24px; + line-height: 1; + } + + .toolbar { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + margin: 18px 0; + } + + .status-text { + margin: 0; + color: var(--muted); + font-size: 13px; + } + + .btn { + border: 1px solid transparent; + background: var(--brand); + color: white; + border-radius: 10px; + padding: 10px 14px; + font-family: inherit; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: background-color 180ms ease, border-color 180ms ease, box-shadow 180ms ease, opacity 180ms ease; + box-shadow: 0 12px 24px rgba(15, 118, 110, 0.25); + } + + .btn:hover { + background: rgba(15, 118, 110, 0.72); + border-color: rgba(255, 255, 255, 0.45); + box-shadow: 0 10px 26px rgba(15, 118, 110, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.28); + backdrop-filter: blur(8px) saturate(135%); + -webkit-backdrop-filter: blur(8px) saturate(135%); + } + .btn:active { + opacity: 0.96; + box-shadow: 0 6px 16px rgba(15, 118, 110, 0.2); + } + .btn.secondary { + background: var(--btn-secondary-bg); + color: var(--text); + border-color: var(--line); + box-shadow: none; + } + .btn.secondary:hover { + background: var(--btn-secondary-hover); + border-color: rgba(148, 163, 184, 0.7); + box-shadow: var(--btn-secondary-shadow); + } + .btn.warn { + background: var(--btn-warn-bg); + color: var(--warn); + border-color: rgba(180, 35, 24, 0.34); + box-shadow: none; + } + .btn.warn:hover { + background: var(--btn-warn-hover); + border-color: rgba(180, 35, 24, 0.45); + box-shadow: var(--btn-warn-shadow); + } + + .toolbar-actions { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; + justify-content: flex-end; + } + + .theme-control { + display: flex; + align-items: center; + gap: 8px; + color: var(--muted); + font-size: 13px; + white-space: nowrap; + } + + .theme-control select { + min-width: 190px; + padding: 9px 10px; + } + + .dashboard { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 14px; + } + + .card { + background: var(--surface); + border: 1px solid var(--card-border); + border-radius: 14px; + padding: 14px; + box-shadow: var(--card-shadow); + } + + .card-top { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 8px; + margin-bottom: 10px; + } + + .card-title { + margin: 0; + font-size: 16px; + line-height: 1.2; + font-family: "Sora", "Space Grotesk", sans-serif; + } + + .card-id { + margin: 6px 0 0; + color: var(--muted); + font-size: 12px; + } + + .pill { + border-radius: 999px; + padding: 4px 10px; + font-size: 12px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.04em; + color: #fff; + } + .pill.pending { background: var(--pending); } + .pill.running { background: var(--running); } + .pill.completed { background: var(--completed); } + + .card-line { + margin: 8px 0; + color: var(--muted); + font-size: 13px; + min-height: 1.3em; + } + + .card-score { + margin: 8px 0; + font-size: 14px; + color: var(--text); + } + + .card-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 12px; + } + + .empty { + background: var(--empty-bg); + border: 1px dashed var(--line); + border-radius: 14px; + padding: 26px; + text-align: center; + color: var(--muted); + } + + .modal { + position: fixed; + inset: 0; + display: none; + align-items: center; + justify-content: center; + background: var(--modal-backdrop); + backdrop-filter: blur(4px); + z-index: 40; + padding: 16px; + } + + .modal.open { display: flex; } + + .modal-panel { + width: min(980px, 100%); + max-height: 92vh; + overflow: auto; + background: var(--surface); + border-radius: 16px; + border: 1px solid var(--modal-border); + box-shadow: 0 24px 64px rgba(2, 6, 23, 0.28); + animation: pop 220ms ease both; + } + + .modal-header { + display: flex; + justify-content: space-between; + align-items: center; + gap: 10px; + border-bottom: 1px solid var(--line); + padding: 14px 16px; + position: sticky; + top: 0; + background: var(--modal-header-bg); + backdrop-filter: blur(2px); + z-index: 3; + } + + .modal-title { + margin: 0; + font-family: "Sora", "Space Grotesk", sans-serif; + font-size: 18px; + } + + .modal-body { + padding: 14px 16px 18px; + } + + .field-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; + margin-bottom: 12px; + } + + label { + display: flex; + flex-direction: column; + gap: 6px; + font-size: 13px; + color: var(--muted); + } + + select, + input[type="checkbox"] { + font: inherit; + } + + select { + padding: 10px; + border-radius: 10px; + border: 1px solid var(--field-border); + background: var(--field-bg); + color: var(--field-text); + } + + .inline { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + margin-bottom: 12px; + font-size: 14px; + color: var(--muted); + } + + .detail-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; + } + + .panel { + border: 1px solid var(--line); + border-radius: 12px; + padding: 10px; + background: var(--surface-soft); + } + + .panel h3 { + margin: 0 0 8px; + font-size: 13px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--brand); + } + + .mono { + margin: 0; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 12px; + line-height: 1.42; + white-space: pre-wrap; + color: var(--mono-text); + max-height: 260px; + overflow: auto; + } + + .scoreboard { + margin: 0 0 12px; + font-family: "Sora", "Space Grotesk", sans-serif; + font-size: 17px; + padding: 10px 12px; + border: 1px solid var(--line); + border-radius: 12px; + background: var(--scoreboard-bg); + } + + @keyframes rise { + from { opacity: 0; transform: translateY(8px); } + to { opacity: 1; transform: translateY(0); } + } + + @keyframes pop { + from { opacity: 0; transform: translateY(10px) scale(0.98); } + to { opacity: 1; transform: translateY(0) scale(1); } + } + + @media (max-width: 920px) { + .hero, + .detail-grid, + .field-grid { + grid-template-columns: 1fr; + } + + .toolbar { + align-items: flex-start; + flex-direction: column; + } + + .toolbar-actions { + width: 100%; + justify-content: space-between; + } + + .theme-control select { + min-width: 160px; + } + + .app { padding: 18px 12px 32px; } + } + -
-
-

MentalNet SoccerCloud

-

Cloudified Soccer Simulation Environment

-
- -
-

Simulation Instances

-
- -
-
+
+
+
+ +

Rust Backend

+

SoccerCloud Web Control Room

+

A modern web frontend backed by the same Rust simulation engine used in CLI and TUI modes.

+
+
+
+
+
Instances
+
0
+
+
+
Running
+
0
+
+
+
Completed
+
0
+
+
+
Available Teams
+
0
+
+
-
-