Continue implementing rooms
Changing lots of things along the way... But that's how it is: Make one change, make more changes to fix the resulting errors and so on.
This commit is contained in:
parent
31ffa5cd67
commit
992e84e67e
12 changed files with 791 additions and 141 deletions
349
Cargo.lock
generated
349
Cargo.lock
generated
|
|
@ -28,6 +28,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
|
|
@ -58,6 +64,12 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.4.3"
|
version = "1.4.3"
|
||||||
|
|
@ -76,12 +88,64 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.73"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5f1fea81f183005ced9e59cdb01737ef2423956dac5a6d731b06b2ecfaa3467"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"bitflags",
|
||||||
|
"clap_derive",
|
||||||
|
"indexmap",
|
||||||
|
"lazy_static",
|
||||||
|
"os_str_bytes",
|
||||||
|
"strsim",
|
||||||
|
"termcolor",
|
||||||
|
"textwrap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5fd1122e63869df2cb309f449da1ad54a7c6dfeb7c7e6ccd8e0825d9eb93bb72"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cove-core"
|
name = "cove-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -121,9 +185,12 @@ name = "cove-tui"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"clap",
|
||||||
"cove-core",
|
"cove-core",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-tungstenite",
|
||||||
"tui",
|
"tui",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -328,6 +395,18 @@ dependencies = [
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.19"
|
version = "0.1.19"
|
||||||
|
|
@ -380,6 +459,16 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instant"
|
name = "instant"
|
||||||
version = "0.1.12"
|
version = "0.1.12"
|
||||||
|
|
@ -395,6 +484,21 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.117"
|
version = "0.2.117"
|
||||||
|
|
@ -484,6 +588,21 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-probe"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "os_str_bytes"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
|
|
@ -533,6 +652,30 @@ version = "0.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.36"
|
version = "1.0.36"
|
||||||
|
|
@ -617,18 +760,109 @@ version = "0.6.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.16.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"spin",
|
||||||
|
"untrusted",
|
||||||
|
"web-sys",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.20.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b323592e3164322f5b193dc4302e4e36cd8d37158a712d664efae1a5c2791700"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"ring",
|
||||||
|
"sct",
|
||||||
|
"webpki",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-native-certs"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ca9ebdfa27d3fc180e42879037b5338ab1c040c06affd00d8338598e7800943"
|
||||||
|
dependencies = [
|
||||||
|
"openssl-probe",
|
||||||
|
"rustls-pemfile",
|
||||||
|
"schannel",
|
||||||
|
"security-framework",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pemfile"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.9"
|
version = "1.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schannel"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sct"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"core-foundation",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
"security-framework-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework-sys"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.136"
|
version = "1.0.136"
|
||||||
|
|
@ -726,6 +960,18 @@ version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.86"
|
version = "1.0.86"
|
||||||
|
|
@ -746,6 +992,12 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.14.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.30"
|
version = "1.0.30"
|
||||||
|
|
@ -811,6 +1063,17 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.23.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b"
|
||||||
|
dependencies = [
|
||||||
|
"rustls",
|
||||||
|
"tokio",
|
||||||
|
"webpki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-stream"
|
name = "tokio-stream"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
|
|
@ -830,8 +1093,12 @@ checksum = "e80b39df6afcc12cdf752398ade96a6b9e99c903dfdc36e53ad10b9c366bca72"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"log",
|
"log",
|
||||||
|
"rustls",
|
||||||
|
"rustls-native-certs",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
"tungstenite",
|
"tungstenite",
|
||||||
|
"webpki",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -860,10 +1127,12 @@ dependencies = [
|
||||||
"httparse",
|
"httparse",
|
||||||
"log",
|
"log",
|
||||||
"rand",
|
"rand",
|
||||||
|
"rustls",
|
||||||
"sha-1",
|
"sha-1",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"url",
|
"url",
|
||||||
"utf-8",
|
"utf-8",
|
||||||
|
"webpki",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -905,6 +1174,12 @@ version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.2.2"
|
version = "2.2.2"
|
||||||
|
|
@ -935,6 +1210,80 @@ version = "0.10.2+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki"
|
||||||
|
version = "0.22.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,6 @@ sha2 = "0.10.1"
|
||||||
thiserror = "1.0.30"
|
thiserror = "1.0.30"
|
||||||
tokio = { version = "1.16.1", features = ["full"] }
|
tokio = { version = "1.16.1", features = ["full"] }
|
||||||
tokio-stream = "0.1.8"
|
tokio-stream = "0.1.8"
|
||||||
tokio-tungstenite = "0.16.1"
|
tokio-tungstenite = { version = "0.16.1", features = [
|
||||||
|
"rustls-tls-native-roots",
|
||||||
|
] }
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||||
use tokio_tungstenite::tungstenite::{self, Message};
|
use tokio_tungstenite::tungstenite::{self, Message};
|
||||||
use tokio_tungstenite::WebSocketStream;
|
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||||
|
|
||||||
use crate::packets::Packet;
|
use crate::packets::Packet;
|
||||||
|
|
||||||
|
|
@ -34,9 +34,10 @@ pub enum Error {
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
|
type WsStream = WebSocketStream<MaybeTlsStream<TcpStream>>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ConnTx {
|
pub struct ConnTx {
|
||||||
peer_addr: SocketAddr,
|
|
||||||
tx: UnboundedSender<Message>,
|
tx: UnboundedSender<Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,16 +50,14 @@ impl fmt::Debug for ConnTx {
|
||||||
impl ConnTx {
|
impl ConnTx {
|
||||||
pub fn send(&self, packet: &Packet) -> Result<()> {
|
pub fn send(&self, packet: &Packet) -> Result<()> {
|
||||||
let str = serde_json::to_string(packet).expect("unserializable packet");
|
let str = serde_json::to_string(packet).expect("unserializable packet");
|
||||||
// TODO Format somewhat nicer?
|
debug!("↑ {}", str.trim()); // TODO Format somewhat nicer?
|
||||||
debug!("<{}> ↑ {}", self.peer_addr, str.trim());
|
|
||||||
self.tx.send(Message::Text(str))?;
|
self.tx.send(Message::Text(str))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ConnRx {
|
pub struct ConnRx {
|
||||||
peer_addr: SocketAddr,
|
ws_rx: SplitStream<WsStream>,
|
||||||
ws_rx: SplitStream<WebSocketStream<TcpStream>>,
|
|
||||||
last_ping_payload: Arc<Mutex<Vec<u8>>>,
|
last_ping_payload: Arc<Mutex<Vec<u8>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,8 +91,7 @@ impl ConnRx {
|
||||||
|
|
||||||
let packet = serde_json::from_str(&str)?;
|
let packet = serde_json::from_str(&str)?;
|
||||||
|
|
||||||
// TODO Format somewhat nicer?
|
debug!("↓ {}", str.trim()); // TODO Format somewhat nicer?
|
||||||
debug!("<{}> ↓ {}", self.peer_addr, str.trim());
|
|
||||||
|
|
||||||
return Ok(Some(packet));
|
return Ok(Some(packet));
|
||||||
}
|
}
|
||||||
|
|
@ -103,7 +101,7 @@ impl ConnRx {
|
||||||
pub struct ConnMaintenance {
|
pub struct ConnMaintenance {
|
||||||
// Shoveling packets into the WS connection
|
// Shoveling packets into the WS connection
|
||||||
rx: UnboundedReceiver<Message>,
|
rx: UnboundedReceiver<Message>,
|
||||||
ws_tx: SplitSink<WebSocketStream<TcpStream>, Message>,
|
ws_tx: SplitSink<WsStream, Message>,
|
||||||
// Pinging and ponging
|
// Pinging and ponging
|
||||||
tx: UnboundedSender<Message>,
|
tx: UnboundedSender<Message>,
|
||||||
ping_delay: Duration,
|
ping_delay: Duration,
|
||||||
|
|
@ -127,7 +125,7 @@ impl ConnMaintenance {
|
||||||
|
|
||||||
async fn shovel(
|
async fn shovel(
|
||||||
rx: UnboundedReceiver<Message>,
|
rx: UnboundedReceiver<Message>,
|
||||||
ws_tx: SplitSink<WebSocketStream<TcpStream>, Message>,
|
ws_tx: SplitSink<WsStream, Message>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
UnboundedReceiverStream::new(rx)
|
UnboundedReceiverStream::new(rx)
|
||||||
.map(Ok)
|
.map(Ok)
|
||||||
|
|
@ -163,22 +161,13 @@ impl ConnMaintenance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(stream: WsStream, ping_delay: Duration) -> Result<(ConnTx, ConnRx, ConnMaintenance)> {
|
||||||
stream: WebSocketStream<TcpStream>,
|
|
||||||
ping_delay: Duration,
|
|
||||||
) -> Result<(ConnTx, ConnRx, ConnMaintenance)> {
|
|
||||||
let peer_addr = stream.get_ref().peer_addr()?;
|
|
||||||
|
|
||||||
let (ws_tx, ws_rx) = stream.split();
|
let (ws_tx, ws_rx) = stream.split();
|
||||||
let (tx, rx) = mpsc::unbounded_channel();
|
let (tx, rx) = mpsc::unbounded_channel();
|
||||||
let last_ping_payload = Arc::new(Mutex::new(vec![]));
|
let last_ping_payload = Arc::new(Mutex::new(vec![]));
|
||||||
|
|
||||||
let conn_tx = ConnTx {
|
let conn_tx = ConnTx { tx: tx.clone() };
|
||||||
peer_addr,
|
|
||||||
tx: tx.clone(),
|
|
||||||
};
|
|
||||||
let conn_rx = ConnRx {
|
let conn_rx = ConnRx {
|
||||||
peer_addr,
|
|
||||||
ws_rx,
|
ws_rx,
|
||||||
last_ping_payload: last_ping_payload.clone(),
|
last_ping_payload: last_ping_payload.clone(),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,30 @@ use crate::macros::packets;
|
||||||
use crate::{Message, MessageId, Session};
|
use crate::{Message, MessageId, Session};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct HelloCmd {
|
pub struct RoomCmd {
|
||||||
pub room: String,
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub enum RoomRpl {
|
||||||
|
Success,
|
||||||
|
InvalidRoom { reason: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct IdentifyCmd {
|
||||||
pub nick: String,
|
pub nick: String,
|
||||||
pub identity: String,
|
pub identity: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum HelloRpl {
|
pub enum IdentifyRpl {
|
||||||
Success {
|
Success {
|
||||||
you: Session,
|
you: Session,
|
||||||
others: Vec<Session>,
|
others: Vec<Session>,
|
||||||
last_message: MessageId,
|
last_message: MessageId,
|
||||||
},
|
},
|
||||||
InvalidRoom {
|
|
||||||
reason: String,
|
|
||||||
},
|
|
||||||
InvalidNick {
|
InvalidNick {
|
||||||
reason: String,
|
reason: String,
|
||||||
},
|
},
|
||||||
|
|
@ -37,7 +44,7 @@ pub struct NickCmd {
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum NickRpl {
|
pub enum NickRpl {
|
||||||
Success,
|
Success { you: Session },
|
||||||
InvalidNick { reason: String },
|
InvalidNick { reason: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +93,8 @@ pub struct SendNtf {
|
||||||
// Create a Cmd enum for all commands, a Rpl enum for all replies and a Ntf enum
|
// Create a Cmd enum for all commands, a Rpl enum for all replies and a Ntf enum
|
||||||
// for all notifications, as well as TryFrom impls for the individual structs.
|
// for all notifications, as well as TryFrom impls for the individual structs.
|
||||||
packets! {
|
packets! {
|
||||||
cmd Hello(HelloCmd, HelloRpl),
|
cmd Room(RoomCmd, RoomRpl),
|
||||||
|
cmd Identify(IdentifyCmd, IdentifyRpl),
|
||||||
cmd Nick(NickCmd, NickRpl),
|
cmd Nick(NickCmd, NickRpl),
|
||||||
cmd Send(SendCmd, SendRpl),
|
cmd Send(SendCmd, SendRpl),
|
||||||
cmd Who(WhoCmd, WhoRpl),
|
cmd Who(WhoCmd, WhoRpl),
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,15 @@ use std::time::Duration;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use cove_core::conn::{self, ConnMaintenance, ConnRx, ConnTx};
|
use cove_core::conn::{self, ConnMaintenance, ConnRx, ConnTx};
|
||||||
use cove_core::packets::{
|
use cove_core::packets::{
|
||||||
Cmd, HelloCmd, HelloRpl, JoinNtf, NickCmd, NickNtf, NickRpl, Packet, PartNtf, SendCmd, SendNtf,
|
Cmd, IdentifyCmd, IdentifyRpl, JoinNtf, NickCmd, NickNtf, NickRpl, Packet, PartNtf, SendCmd,
|
||||||
SendRpl, WhoCmd, WhoRpl,
|
SendNtf, SendRpl, WhoCmd, WhoRpl,
|
||||||
};
|
};
|
||||||
use cove_core::{Identity, Message, MessageId, Session, SessionId};
|
use cove_core::{Identity, Message, MessageId, Session, SessionId};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use tokio::net::{TcpListener, TcpStream};
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
use tokio_tungstenite::MaybeTlsStream;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Client {
|
struct Client {
|
||||||
|
|
@ -155,7 +156,12 @@ impl ServerSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.session.nick = cmd.nick.clone();
|
self.session.nick = cmd.nick.clone();
|
||||||
self.tx.send(&Packet::rpl(id, NickRpl::Success))?;
|
self.tx.send(&Packet::rpl(
|
||||||
|
id,
|
||||||
|
NickRpl::Success {
|
||||||
|
you: self.session.clone(),
|
||||||
|
},
|
||||||
|
))?;
|
||||||
self.room.lock().await.nick(self.session.id, cmd.nick);
|
self.room.lock().await.nick(self.session.id, cmd.nick);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -189,7 +195,8 @@ impl ServerSession {
|
||||||
async fn handle_packet(&mut self, packet: Packet) -> anyhow::Result<()> {
|
async fn handle_packet(&mut self, packet: Packet) -> anyhow::Result<()> {
|
||||||
match packet {
|
match packet {
|
||||||
Packet::Cmd { id, cmd } => match cmd {
|
Packet::Cmd { id, cmd } => match cmd {
|
||||||
Cmd::Hello(_) => Err(anyhow!("unexpected Hello cmd")),
|
Cmd::Room(_) => Err(anyhow!("unexpected Room cmd")),
|
||||||
|
Cmd::Identify(_) => Err(anyhow!("unexpected Identify cmd")),
|
||||||
Cmd::Nick(cmd) => self.handle_nick(id, cmd).await,
|
Cmd::Nick(cmd) => self.handle_nick(id, cmd).await,
|
||||||
Cmd::Send(cmd) => self.handle_send(id, cmd).await,
|
Cmd::Send(cmd) => self.handle_send(id, cmd).await,
|
||||||
Cmd::Who(cmd) => self.handle_who(id, cmd).await,
|
Cmd::Who(cmd) => self.handle_who(id, cmd).await,
|
||||||
|
|
@ -228,90 +235,92 @@ impl Server {
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_hello(
|
// async fn handle_hello(
|
||||||
&self,
|
// &self,
|
||||||
tx: &ConnTx,
|
// tx: &ConnTx,
|
||||||
id: u64,
|
// id: u64,
|
||||||
cmd: HelloCmd,
|
// cmd: IdentifyCmd,
|
||||||
) -> anyhow::Result<Option<(String, Session)>> {
|
// ) -> anyhow::Result<Option<(String, Session)>> {
|
||||||
if let Some(reason) = util::check_room(&cmd.room) {
|
// if let Some(reason) = util::check_room(&cmd.room) {
|
||||||
tx.send(&Packet::rpl(id, HelloRpl::InvalidRoom { reason }))?;
|
// tx.send(&Packet::rpl(id, IdentifyRpl::InvalidRoom { reason }))?;
|
||||||
return Ok(None);
|
// return Ok(None);
|
||||||
}
|
// }
|
||||||
if let Some(reason) = util::check_nick(&cmd.nick) {
|
// if let Some(reason) = util::check_nick(&cmd.nick) {
|
||||||
tx.send(&Packet::rpl(id, HelloRpl::InvalidNick { reason }))?;
|
// tx.send(&Packet::rpl(id, IdentifyRpl::InvalidNick { reason }))?;
|
||||||
return Ok(None);
|
// return Ok(None);
|
||||||
}
|
// }
|
||||||
if let Some(reason) = util::check_identity(&cmd.identity) {
|
// if let Some(reason) = util::check_identity(&cmd.identity) {
|
||||||
tx.send(&Packet::rpl(id, HelloRpl::InvalidIdentity { reason }))?;
|
// tx.send(&Packet::rpl(id, IdentifyRpl::InvalidIdentity { reason }))?;
|
||||||
return Ok(None);
|
// return Ok(None);
|
||||||
}
|
// }
|
||||||
|
|
||||||
let session = Session {
|
// let session = Session {
|
||||||
id: SessionId::of(&format!("{}", rand::thread_rng().gen::<u64>())),
|
// id: SessionId::of(&format!("{}", rand::thread_rng().gen::<u64>())),
|
||||||
nick: cmd.nick,
|
// nick: cmd.nick,
|
||||||
identity: Identity::of(&cmd.identity),
|
// identity: Identity::of(&cmd.identity),
|
||||||
};
|
// };
|
||||||
|
|
||||||
Ok(Some((cmd.room, session)))
|
// Ok(Some((cmd.room, session)))
|
||||||
}
|
// }
|
||||||
|
|
||||||
async fn greet(&self, tx: ConnTx, mut rx: ConnRx) -> anyhow::Result<ServerSession> {
|
// async fn greet(&self, tx: ConnTx, mut rx: ConnRx) -> anyhow::Result<ServerSession> {
|
||||||
let (id, room, session) = loop {
|
// let (id, room, session) = loop {
|
||||||
let (id, cmd) = match rx.recv().await? {
|
// let (id, cmd) = match rx.recv().await? {
|
||||||
Some(Packet::Cmd {
|
// Some(Packet::Cmd {
|
||||||
id,
|
// id,
|
||||||
cmd: Cmd::Hello(cmd),
|
// cmd: Cmd::Hello(cmd),
|
||||||
}) => (id, cmd),
|
// }) => (id, cmd),
|
||||||
Some(_) => return Err(anyhow!("not a Hello packet")),
|
// Some(_) => return Err(anyhow!("not a Hello packet")),
|
||||||
None => return Err(anyhow!("connection closed during greeting")),
|
// None => return Err(anyhow!("connection closed during greeting")),
|
||||||
};
|
// };
|
||||||
|
|
||||||
if let Some((room, session)) = self.handle_hello(&tx, id, cmd).await? {
|
// if let Some((room, session)) = self.handle_hello(&tx, id, cmd).await? {
|
||||||
break (id, room, session);
|
// break (id, room, session);
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
let room = self.room(room).await;
|
// let room = self.room(room).await;
|
||||||
|
|
||||||
{
|
// {
|
||||||
let mut room = room.lock().await;
|
// let mut room = room.lock().await;
|
||||||
|
|
||||||
let you = session.clone();
|
// let you = session.clone();
|
||||||
let others = room
|
// let others = room
|
||||||
.clients
|
// .clients
|
||||||
.values()
|
// .values()
|
||||||
.map(|client| client.session.clone())
|
// .map(|client| client.session.clone())
|
||||||
.collect::<Vec<_>>();
|
// .collect::<Vec<_>>();
|
||||||
let last_message = room.last_message;
|
// let last_message = room.last_message;
|
||||||
|
|
||||||
tx.send(&Packet::rpl(
|
// tx.send(&Packet::rpl(
|
||||||
id,
|
// id,
|
||||||
HelloRpl::Success {
|
// IdentifyRpl::Success {
|
||||||
you,
|
// you,
|
||||||
others,
|
// others,
|
||||||
last_message,
|
// last_message,
|
||||||
},
|
// },
|
||||||
))?;
|
// ))?;
|
||||||
|
|
||||||
room.join(Client {
|
// room.join(Client {
|
||||||
session: session.clone(),
|
// session: session.clone(),
|
||||||
send: tx.clone(),
|
// send: tx.clone(),
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// Ok(ServerSession {
|
||||||
|
// tx,
|
||||||
|
// rx,
|
||||||
|
// room,
|
||||||
|
// session,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
Ok(ServerSession {
|
|
||||||
tx,
|
|
||||||
rx,
|
|
||||||
room,
|
|
||||||
session,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
async fn greet_and_run(&self, tx: ConnTx, rx: ConnRx) -> anyhow::Result<()> {
|
async fn greet_and_run(&self, tx: ConnTx, rx: ConnRx) -> anyhow::Result<()> {
|
||||||
let mut session = self.greet(tx, rx).await?;
|
// let mut session = self.greet(tx, rx).await?;
|
||||||
let result = session.run().await;
|
// let result = session.run().await;
|
||||||
session.room.lock().await.part(session.session.id);
|
// session.room.lock().await.part(session.session.id);
|
||||||
result
|
// result
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper for [`ConnMaintenance::perform`] so it returns an
|
/// Wrapper for [`ConnMaintenance::perform`] so it returns an
|
||||||
|
|
@ -322,6 +331,7 @@ impl Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_conn(&self, stream: TcpStream) -> anyhow::Result<()> {
|
async fn handle_conn(&self, stream: TcpStream) -> anyhow::Result<()> {
|
||||||
|
let stream = MaybeTlsStream::Plain(stream);
|
||||||
let stream = tokio_tungstenite::accept_async(stream).await?;
|
let stream = tokio_tungstenite::accept_async(stream).await?;
|
||||||
let (tx, rx, maintenance) = conn::new(stream, Duration::from_secs(10))?;
|
let (tx, rx, maintenance) = conn::new(stream, Duration::from_secs(10))?;
|
||||||
tokio::try_join!(self.greet_and_run(tx, rx), Self::maintain(maintenance))?;
|
tokio::try_join!(self.greet_and_run(tx, rx), Self::maintain(maintenance))?;
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,15 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.53"
|
anyhow = "1.0.53"
|
||||||
|
clap = { version = "3.1.0", features = ["derive"] }
|
||||||
cove-core = { path = "../cove-core" }
|
cove-core = { path = "../cove-core" }
|
||||||
crossterm = "0.22.1"
|
crossterm = "0.22.1"
|
||||||
# futures = "0.3.21"
|
# futures = "0.3.21"
|
||||||
# serde_json = "1.0.78"
|
# serde_json = "1.0.78"
|
||||||
# thiserror = "1.0.30"
|
thiserror = "1.0.30"
|
||||||
tokio = { version = "1.16.1", features = ["full"] }
|
tokio = { version = "1.16.1", features = ["full"] }
|
||||||
# tokio-stream = "0.1.8"
|
# tokio-stream = "0.1.8"
|
||||||
# tokio-tungstenite = "0.16.1"
|
tokio-tungstenite = { version = "0.16.1", features = [
|
||||||
|
"rustls-tls-native-roots",
|
||||||
|
] }
|
||||||
tui = "0.17.0"
|
tui = "0.17.0"
|
||||||
|
|
|
||||||
24
cove-tui/src/config.rs
Normal file
24
cove-tui/src/config.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct Args {
|
||||||
|
#[clap(long, default_value_t = String::from("wss://plugh.de/cove/"))]
|
||||||
|
cove_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
pub cove_url: String,
|
||||||
|
pub timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn load() -> Self {
|
||||||
|
let args = Args::parse();
|
||||||
|
Self {
|
||||||
|
cove_url: args.cove_url,
|
||||||
|
timeout: Duration::from_secs(10),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
mod replies;
|
mod replies;
|
||||||
mod room;
|
mod room;
|
||||||
|
mod config;
|
||||||
|
mod never;
|
||||||
|
|
||||||
use std::io::{self, Stdout};
|
use std::io::{self, Stdout};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use config::Config;
|
||||||
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
|
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
|
||||||
use crossterm::execute;
|
use crossterm::execute;
|
||||||
use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen};
|
use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen};
|
||||||
|
|
@ -23,6 +26,8 @@ async fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> anyhow::Resul
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
let config = Config::load();
|
||||||
|
|
||||||
let mut terminal = Terminal::new(CrosstermBackend::new(io::stdout()))?;
|
let mut terminal = Terminal::new(CrosstermBackend::new(io::stdout()))?;
|
||||||
|
|
||||||
crossterm::terminal::enable_raw_mode()?;
|
crossterm::terminal::enable_raw_mode()?;
|
||||||
|
|
|
||||||
2
cove-tui/src/never.rs
Normal file
2
cove-tui/src/never.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
// TODO Replace with `!` when it is stabilised
|
||||||
|
pub enum Never {}
|
||||||
|
|
@ -6,8 +6,11 @@ use std::time::Duration;
|
||||||
use tokio::sync::oneshot::{self, Receiver, Sender};
|
use tokio::sync::oneshot::{self, Receiver, Sender};
|
||||||
use tokio::time;
|
use tokio::time;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error("timed out")]
|
||||||
TimedOut,
|
TimedOut,
|
||||||
|
#[error("canceled")]
|
||||||
Canceled,
|
Canceled,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,7 +38,14 @@ pub struct Replies<I, R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Eq + Hash, R> Replies<I, R> {
|
impl<I: Eq + Hash, R> Replies<I, R> {
|
||||||
pub async fn wait_for(&mut self, id: I) -> PendingReply<R> {
|
pub fn new(timeout: Duration) -> Self {
|
||||||
|
Self {
|
||||||
|
timeout,
|
||||||
|
pending: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait_for(&mut self, id: I) -> PendingReply<R> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
self.pending.insert(id, tx);
|
self.pending.insert(id, tx);
|
||||||
PendingReply {
|
PendingReply {
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,280 @@
|
||||||
|
use std::any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use cove_core::conn::ConnTx;
|
use anyhow::bail;
|
||||||
|
use cove_core::conn::{self, ConnMaintenance, ConnRx, ConnTx};
|
||||||
|
use cove_core::packets::{
|
||||||
|
Cmd, IdentifyCmd, IdentifyRpl, JoinNtf, NickRpl, Ntf, Packet, RoomCmd, RoomRpl, Rpl, SendRpl,
|
||||||
|
WhoRpl,
|
||||||
|
};
|
||||||
use cove_core::{Session, SessionId};
|
use cove_core::{Session, SessionId};
|
||||||
use tokio::sync::oneshot::{self, Sender};
|
use tokio::sync::oneshot::{self, Sender};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
pub enum ConnectedState {
|
use crate::config::Config;
|
||||||
ChoosingNick,
|
use crate::never::Never;
|
||||||
Identifying,
|
use crate::replies::{self, Replies};
|
||||||
Online,
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("not connected")]
|
||||||
|
NotConnected,
|
||||||
|
#[error("not present")]
|
||||||
|
NotPresent,
|
||||||
|
#[error("incorrect reply type")]
|
||||||
|
IncorrectReplyType,
|
||||||
|
#[error("{0}")]
|
||||||
|
Conn(#[from] conn::Error),
|
||||||
|
#[error("{0}")]
|
||||||
|
Replies(#[from] replies::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum RoomState {
|
/// State for when a websocket connection exists.
|
||||||
Connecting,
|
struct Connected {
|
||||||
Reconnecting,
|
tx: ConnTx,
|
||||||
Connected { state: ConnectedState, tx: ConnTx },
|
next_id: u64,
|
||||||
DoesNotExist,
|
replies: Replies<u64, Rpl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// State for when a client has fully joined a room.
|
||||||
|
struct Present {
|
||||||
|
session: Session,
|
||||||
|
others: HashMap<SessionId, Session>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
/// No action required by the UI.
|
||||||
|
Nominal,
|
||||||
|
/// User must enter a nick.
|
||||||
|
NickRequired,
|
||||||
|
/// Identifying to the server. No action required by the UI.
|
||||||
|
Identifying,
|
||||||
|
CouldNotConnect,
|
||||||
|
InvalidRoom(String),
|
||||||
|
InvalidNick(String),
|
||||||
|
InvalidIdentity(String),
|
||||||
|
InvalidContent(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Room {
|
pub struct Room {
|
||||||
name: String,
|
name: String,
|
||||||
state: RoomState,
|
identity: String,
|
||||||
nick: Option<String>,
|
initial_nick: Option<String>,
|
||||||
others: HashMap<SessionId, Session>,
|
status: Status,
|
||||||
stop: Sender<()>,
|
connected: Option<Connected>,
|
||||||
|
present: Option<Present>,
|
||||||
|
still_alive: Sender<Never>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Room {
|
impl Room {
|
||||||
pub async fn create(name: String) -> Arc<Mutex<Self>> {
|
pub async fn new(
|
||||||
|
name: String,
|
||||||
|
identity: String,
|
||||||
|
initial_nick: Option<String>,
|
||||||
|
config: &'static Config,
|
||||||
|
) -> Arc<Mutex<Self>> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
|
|
||||||
let room = Self {
|
let room = Arc::new(Mutex::new(Self {
|
||||||
name,
|
name,
|
||||||
state: RoomState::Connecting,
|
identity,
|
||||||
nick: None,
|
initial_nick,
|
||||||
others: HashMap::new(),
|
status: Status::Nominal,
|
||||||
stop: tx,
|
connected: None,
|
||||||
};
|
present: None,
|
||||||
let room = Arc::new(Mutex::new(room));
|
still_alive: tx,
|
||||||
|
}));
|
||||||
|
|
||||||
let room_clone = room.clone();
|
let room_clone = room.clone();
|
||||||
tokio::spawn(async {
|
tokio::spawn(async move {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = rx => {},
|
_ = rx => {}
|
||||||
_ = Self::connect(room_clone) => {}
|
_ = Self::bg_task(room_clone, config) => {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
room
|
room
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn connect(room: Arc<Mutex<Self>>) {
|
async fn bg_task(room: Arc<Mutex<Room>>, config: &'static Config) {
|
||||||
todo!()
|
let mut room_verified = false;
|
||||||
|
loop {
|
||||||
|
if let Ok((tx, rx, mt)) = Self::connect(&config.cove_url, config.timeout).await {
|
||||||
|
{
|
||||||
|
let mut room = room.lock().await;
|
||||||
|
room.status = Status::Nominal;
|
||||||
|
room.connected = Some(Connected {
|
||||||
|
tx,
|
||||||
|
next_id: 0,
|
||||||
|
replies: Replies::new(config.timeout),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(self) {
|
tokio::select! {
|
||||||
// If the send goes wrong because the other end has hung up, it's
|
_ = mt.perform() => {}
|
||||||
// already stopped and there's nothing to do.
|
_ = Self::receive(room.clone(), rx, &mut room_verified) => {}
|
||||||
let _ = self.stop.send(());
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !room_verified {
|
||||||
|
room.lock().await.status = Status::CouldNotConnect;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn connect(
|
||||||
|
url: &str,
|
||||||
|
timeout: Duration,
|
||||||
|
) -> anyhow::Result<(ConnTx, ConnRx, ConnMaintenance)> {
|
||||||
|
let stream = tokio_tungstenite::connect_async(url).await?.0;
|
||||||
|
let conn = conn::new(stream, timeout)?;
|
||||||
|
Ok(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn receive(
|
||||||
|
room: Arc<Mutex<Room>>,
|
||||||
|
mut rx: ConnRx,
|
||||||
|
room_verified: &mut bool,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
while let Some(packet) = rx.recv().await? {
|
||||||
|
match packet {
|
||||||
|
Packet::Cmd { .. } => {} // Ignore, the server never sends commands
|
||||||
|
Packet::Rpl { id, rpl } => {
|
||||||
|
room.lock().await.on_rpl(&room, id, rpl, room_verified)?;
|
||||||
|
}
|
||||||
|
Packet::Ntf { ntf } => room.lock().await.on_ntf(ntf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_rpl(
|
||||||
|
&mut self,
|
||||||
|
room: &Arc<Mutex<Room>>,
|
||||||
|
id: u64,
|
||||||
|
rpl: Rpl,
|
||||||
|
room_verified: &mut bool,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
match &rpl {
|
||||||
|
Rpl::Room(RoomRpl::Success) => {
|
||||||
|
*room_verified = true;
|
||||||
|
if let Some(nick) = &self.initial_nick {
|
||||||
|
tokio::spawn(Self::identify(
|
||||||
|
room.clone(),
|
||||||
|
nick.clone(),
|
||||||
|
self.identity.clone(),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
self.status = Status::NickRequired;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rpl::Room(RoomRpl::InvalidRoom { reason }) => {
|
||||||
|
self.status = Status::InvalidRoom(reason.clone());
|
||||||
|
anyhow::bail!("invalid room");
|
||||||
|
}
|
||||||
|
Rpl::Identify(IdentifyRpl::Success {
|
||||||
|
you,
|
||||||
|
others,
|
||||||
|
last_message,
|
||||||
|
}) => {
|
||||||
|
let others = others
|
||||||
|
.iter()
|
||||||
|
.map(|session| (session.id, session.clone()))
|
||||||
|
.collect();
|
||||||
|
self.present = Some(Present {
|
||||||
|
session: you.clone(),
|
||||||
|
others,
|
||||||
|
});
|
||||||
|
// TODO Send last message to store
|
||||||
|
}
|
||||||
|
Rpl::Identify(IdentifyRpl::InvalidNick { reason }) => {
|
||||||
|
self.status = Status::InvalidNick(reason.clone());
|
||||||
|
}
|
||||||
|
Rpl::Identify(IdentifyRpl::InvalidIdentity { reason }) => {
|
||||||
|
self.status = Status::InvalidIdentity(reason.clone());
|
||||||
|
}
|
||||||
|
Rpl::Nick(NickRpl::Success { you }) => {
|
||||||
|
if let Some(present) = &mut self.present {
|
||||||
|
present.session = you.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rpl::Nick(NickRpl::InvalidNick { reason }) => {
|
||||||
|
self.status = Status::InvalidNick(reason.clone());
|
||||||
|
}
|
||||||
|
Rpl::Send(SendRpl::Success { message }) => {
|
||||||
|
// TODO Send message to store
|
||||||
|
}
|
||||||
|
Rpl::Send(SendRpl::InvalidContent { reason }) => {
|
||||||
|
self.status = Status::InvalidContent(reason.clone());
|
||||||
|
}
|
||||||
|
Rpl::Who(WhoRpl { you, others }) => {
|
||||||
|
if let Some(present) = &mut self.present {
|
||||||
|
present.session = you.clone();
|
||||||
|
present.others = others
|
||||||
|
.iter()
|
||||||
|
.map(|session| (session.id, session.clone()))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(connected) = &mut self.connected {
|
||||||
|
connected.replies.complete(&id, rpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_ntf(&mut self, ntf: Ntf) {
|
||||||
|
match ntf {
|
||||||
|
Ntf::Join(join) => {
|
||||||
|
if let Some(present) = &mut self.present {
|
||||||
|
present.others.insert(join.who.id, join.who);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ntf::Nick(nick) => {
|
||||||
|
if let Some(present) = &mut self.present {
|
||||||
|
present.others.insert(nick.who.id, nick.who);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ntf::Part(part) => {
|
||||||
|
if let Some(present) = &mut self.present {
|
||||||
|
present.others.remove(&part.who.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ntf::Send(_) => {
|
||||||
|
// TODO Send message to store
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cmd<C, R>(room: &Mutex<Room>, cmd: C) -> Result<R, Error>
|
||||||
|
where
|
||||||
|
C: Into<Cmd>,
|
||||||
|
Rpl: TryInto<R>,
|
||||||
|
{
|
||||||
|
let token = {
|
||||||
|
let mut room = room.lock().await;
|
||||||
|
let connected = room.connected.as_mut().ok_or(Error::NotConnected)?;
|
||||||
|
|
||||||
|
let id = connected.next_id;
|
||||||
|
connected.next_id += 1;
|
||||||
|
|
||||||
|
let pending_reply = connected.replies.wait_for(id);
|
||||||
|
connected.tx.send(&Packet::cmd(id, cmd.into()))?;
|
||||||
|
pending_reply
|
||||||
|
};
|
||||||
|
|
||||||
|
let rpl = token.get().await?;
|
||||||
|
let rpl = rpl.try_into().map_err(|_| Error::IncorrectReplyType)?;
|
||||||
|
Ok(rpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn identify(room: Arc<Mutex<Room>>, nick: String, identity: String) -> Result<(), Error> {
|
||||||
|
let result: IdentifyRpl = Self::cmd(&room, IdentifyCmd { nick, identity }).await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
31
room.md
Normal file
31
room.md
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
- Determine room name
|
||||||
|
- Connect for the first time
|
||||||
|
- If connection fails: Show error, done
|
||||||
|
- Set room
|
||||||
|
- If room is invalid: Show error, done
|
||||||
|
- If no nick is set by default: Let user choose nick
|
||||||
|
- Identify yourself
|
||||||
|
- If nick is invalid: Show error, let user edit nick
|
||||||
|
- If identity is invalid: Show error, done
|
||||||
|
- Listen to events, send commands
|
||||||
|
- Reconnect
|
||||||
|
- If connection fails: Show error, done
|
||||||
|
- Set room
|
||||||
|
- If room is invalid: Show error, done
|
||||||
|
- Identify yourself
|
||||||
|
- If nick is invalid: Show error, let user edit nick
|
||||||
|
- If identity is invalid: Show error, done
|
||||||
|
- Listen to events, send commands
|
||||||
|
|
||||||
|
General state:
|
||||||
|
- Initial nick (optional)
|
||||||
|
- A way to stop the entire room
|
||||||
|
|
||||||
|
State present when WS connection exists:
|
||||||
|
- Connection itself
|
||||||
|
- Next command id
|
||||||
|
- Replies
|
||||||
|
|
||||||
|
State present when fully connected:
|
||||||
|
- Own session
|
||||||
|
- Others
|
||||||
Loading…
Add table
Add a link
Reference in a new issue