client: implements event/updates websocket
This commit is contained in:
224
Cargo.lock
generated
224
Cargo.lock
generated
@@ -181,6 +181,15 @@ version = "2.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.16.0"
|
version = "3.16.0"
|
||||||
@@ -189,9 +198,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.6.0"
|
version = "1.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
@@ -297,6 +306,25 @@ version = "0.8.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "csv"
|
name = "csv"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
@@ -363,6 +391,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "data-encoding"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dbus"
|
name = "dbus"
|
||||||
version = "0.9.7"
|
version = "0.9.7"
|
||||||
@@ -471,6 +505,16 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "directories"
|
name = "directories"
|
||||||
version = "6.0.0"
|
version = "6.0.0"
|
||||||
@@ -657,12 +701,23 @@ checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
@@ -671,7 +726,19 @@ checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi",
|
||||||
|
"wasi 0.14.2+wasi-0.2.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -691,7 +758,7 @@ dependencies = [
|
|||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http 0.2.12",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -743,6 +810,17 @@ dependencies = [
|
|||||||
"itoa",
|
"itoa",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"itoa",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http-body"
|
name = "http-body"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@@ -750,7 +828,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"http",
|
"http 0.2.12",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -777,7 +855,7 @@ dependencies = [
|
|||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2",
|
"h2",
|
||||||
"http",
|
"http 0.2.12",
|
||||||
"http-body",
|
"http-body",
|
||||||
"httparse",
|
"httparse",
|
||||||
"httpdate",
|
"httpdate",
|
||||||
@@ -907,6 +985,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"ctor",
|
"ctor",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"futures-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper-tls",
|
"hyper-tls",
|
||||||
"log",
|
"log",
|
||||||
@@ -915,6 +994,8 @@ dependencies = [
|
|||||||
"serde_plain",
|
"serde_plain",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-tungstenite",
|
||||||
|
"tungstenite",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -967,6 +1048,8 @@ dependencies = [
|
|||||||
"dbus-codegen",
|
"dbus-codegen",
|
||||||
"dbus-tree",
|
"dbus-tree",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
|
"env_logger",
|
||||||
|
"futures-util",
|
||||||
"kordophone",
|
"kordophone",
|
||||||
"kordophone-db",
|
"kordophone-db",
|
||||||
"log",
|
"log",
|
||||||
@@ -1083,7 +1166,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi 0.3.9",
|
"hermit-abi 0.3.9",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1297,6 +1380,12 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@@ -1304,8 +1393,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha 0.3.1",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha 0.9.0",
|
||||||
|
"rand_core 0.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1315,7 +1414,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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 0.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1324,7 +1433,16 @@ version = "0.6.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.14",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1342,7 +1460,7 @@ version = "0.4.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.14",
|
||||||
"libredox",
|
"libredox",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
@@ -1353,7 +1471,7 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.14",
|
||||||
"libredox",
|
"libredox",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
]
|
]
|
||||||
@@ -1505,6 +1623,17 @@ dependencies = [
|
|||||||
"serde",
|
"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]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.1"
|
version = "1.4.1"
|
||||||
@@ -1713,6 +1842,18 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-tungstenite"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
|
||||||
|
dependencies = [
|
||||||
|
"futures-util",
|
||||||
|
"log",
|
||||||
|
"tokio",
|
||||||
|
"tungstenite",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.10"
|
version = "0.7.10"
|
||||||
@@ -1792,12 +1933,35 @@ version = "0.2.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tungstenite"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"data-encoding",
|
||||||
|
"http 1.3.1",
|
||||||
|
"httparse",
|
||||||
|
"log",
|
||||||
|
"rand 0.9.1",
|
||||||
|
"sha1",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"utf-8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typed-arena"
|
name = "typed-arena"
|
||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
@@ -1810,6 +1974,12 @@ version = "0.1.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf-8"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@@ -1822,8 +1992,8 @@ version = "1.11.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
|
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.14",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
"uuid-macro-internal",
|
"uuid-macro-internal",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1850,6 +2020,12 @@ version = "0.8.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -1865,6 +2041,15 @@ version = "0.11.0+wasi-snapshot-preview1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.14.2+wasi-0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen-rt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.95"
|
version = "0.2.95"
|
||||||
@@ -2108,6 +2293,15 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rt"
|
||||||
|
version = "0.39.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.5.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xml-rs"
|
name = "xml-rs"
|
||||||
version = "0.8.25"
|
version = "0.8.25"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ base64 = "0.22.1"
|
|||||||
chrono = { version = "0.4.38", features = ["serde"] }
|
chrono = { version = "0.4.38", features = ["serde"] }
|
||||||
ctor = "0.2.8"
|
ctor = "0.2.8"
|
||||||
env_logger = "0.11.5"
|
env_logger = "0.11.5"
|
||||||
|
futures-util = "0.3.31"
|
||||||
hyper = { version = "0.14", features = ["full"] }
|
hyper = { version = "0.14", features = ["full"] }
|
||||||
hyper-tls = "0.5.0"
|
hyper-tls = "0.5.0"
|
||||||
log = { version = "0.4.21", features = [] }
|
log = { version = "0.4.21", features = [] }
|
||||||
@@ -19,4 +20,6 @@ serde_json = "1.0.91"
|
|||||||
serde_plain = "1.0.2"
|
serde_plain = "1.0.2"
|
||||||
time = { version = "0.3.17", features = ["parsing", "serde"] }
|
time = { version = "0.3.17", features = ["parsing", "serde"] }
|
||||||
tokio = { version = "1.37.0", features = ["full"] }
|
tokio = { version = "1.37.0", features = ["full"] }
|
||||||
|
tokio-tungstenite = "0.26.2"
|
||||||
|
tungstenite = "0.26.2"
|
||||||
uuid = { version = "1.6.1", features = ["v4", "fast-rng", "macro-diagnostics"] }
|
uuid = { version = "1.6.1", features = ["v4", "fast-rng", "macro-diagnostics"] }
|
||||||
|
|||||||
45
kordophone/src/api/auth.rs
Normal file
45
kordophone/src/api/auth.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
use crate::api::Credentials;
|
||||||
|
use crate::api::JwtToken;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AuthenticationStore {
|
||||||
|
async fn get_credentials(&mut self) -> Option<Credentials>;
|
||||||
|
async fn get_token(&mut self) -> Option<JwtToken>;
|
||||||
|
async fn set_token(&mut self, token: JwtToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InMemoryAuthenticationStore {
|
||||||
|
credentials: Option<Credentials>,
|
||||||
|
token: Option<JwtToken>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InMemoryAuthenticationStore {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InMemoryAuthenticationStore {
|
||||||
|
pub fn new(credentials: Option<Credentials>) -> Self {
|
||||||
|
Self {
|
||||||
|
credentials,
|
||||||
|
token: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AuthenticationStore for InMemoryAuthenticationStore {
|
||||||
|
async fn get_credentials(&mut self) -> Option<Credentials> {
|
||||||
|
self.credentials.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_token(&mut self) -> Option<JwtToken> {
|
||||||
|
self.token.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_token(&mut self, token: JwtToken) {
|
||||||
|
self.token = Some(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
kordophone/src/api/event_socket.rs
Normal file
17
kordophone/src/api/event_socket.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
use crate::model::update::UpdateItem;
|
||||||
|
use crate::model::event::Event;
|
||||||
|
use futures_util::stream::Stream;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait EventSocket {
|
||||||
|
type Error;
|
||||||
|
type EventStream: Stream<Item = Result<Event, Self::Error>>;
|
||||||
|
type UpdateStream: Stream<Item = Result<Vec<UpdateItem>, Self::Error>>;
|
||||||
|
|
||||||
|
/// Modern event pipeline
|
||||||
|
async fn events(self) -> Self::EventStream;
|
||||||
|
|
||||||
|
/// Raw update items from the v1 API.
|
||||||
|
async fn raw_updates(self) -> Self::UpdateStream;
|
||||||
|
}
|
||||||
@@ -4,13 +4,25 @@ extern crate serde;
|
|||||||
use std::{path::PathBuf, str};
|
use std::{path::PathBuf, str};
|
||||||
|
|
||||||
use crate::api::AuthenticationStore;
|
use crate::api::AuthenticationStore;
|
||||||
|
use crate::api::event_socket::EventSocket;
|
||||||
use hyper::{Body, Client, Method, Request, Uri};
|
use hyper::{Body, Client, Method, Request, Uri};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
|
||||||
|
use futures_util::{StreamExt, TryStreamExt};
|
||||||
|
use futures_util::stream::{SplitStream, SplitSink, TryFilterMap, MapErr, Stream};
|
||||||
|
use futures_util::stream::Map;
|
||||||
|
use futures_util::stream::BoxStream;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
use tokio_tungstenite::connect_async;
|
||||||
|
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
model::{Conversation, ConversationID, JwtToken, Message, MessageID},
|
model::{Conversation, ConversationID, JwtToken, Message, MessageID, UpdateItem, Event},
|
||||||
APIInterface
|
APIInterface
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,6 +75,12 @@ impl From <serde_json::Error> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From <tungstenite::Error> for Error {
|
||||||
|
fn from(err: tungstenite::Error) -> Error {
|
||||||
|
Error::ClientError(err.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
trait AuthBuilder {
|
trait AuthBuilder {
|
||||||
fn with_auth(self, token: &Option<JwtToken>) -> Self;
|
fn with_auth(self, token: &Option<JwtToken>) -> Self;
|
||||||
}
|
}
|
||||||
@@ -90,6 +108,58 @@ impl<B> AuthSetting for hyper::http::Request<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WebsocketSink = SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, tungstenite::Message>;
|
||||||
|
type WebsocketStream = SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>;
|
||||||
|
|
||||||
|
pub struct WebsocketEventSocket {
|
||||||
|
_sink: WebsocketSink,
|
||||||
|
stream: WebsocketStream,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebsocketEventSocket {
|
||||||
|
pub fn new(socket: WebSocketStream<MaybeTlsStream<TcpStream>>) -> Self {
|
||||||
|
let (sink, stream) = socket.split();
|
||||||
|
Self { _sink: sink, stream }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebsocketEventSocket {
|
||||||
|
fn raw_update_stream(self) -> impl Stream<Item = Result<Vec<UpdateItem>, Error>> {
|
||||||
|
self.stream
|
||||||
|
.map_err(Error::from)
|
||||||
|
.try_filter_map(|msg| async move {
|
||||||
|
match msg {
|
||||||
|
tungstenite::Message::Text(text) => {
|
||||||
|
serde_json::from_str::<Vec<UpdateItem>>(&text)
|
||||||
|
.map(Some)
|
||||||
|
.map_err(Error::from)
|
||||||
|
}
|
||||||
|
_ => Ok(None)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl EventSocket for WebsocketEventSocket {
|
||||||
|
type Error = Error;
|
||||||
|
type EventStream = BoxStream<'static, Result<Event, Error>>;
|
||||||
|
type UpdateStream = BoxStream<'static, Result<Vec<UpdateItem>, Error>>;
|
||||||
|
|
||||||
|
async fn events(self) -> Self::EventStream {
|
||||||
|
use futures_util::stream::iter;
|
||||||
|
|
||||||
|
self.raw_update_stream()
|
||||||
|
.map_ok(|updates| iter(updates.into_iter().map(|update| Ok(Event::from(update)))))
|
||||||
|
.try_flatten()
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn raw_updates(self) -> Self::UpdateStream {
|
||||||
|
self.raw_update_stream().boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<K: AuthenticationStore + Send + Sync> APIInterface for HTTPAPIClient<K> {
|
impl<K: AuthenticationStore + Send + Sync> APIInterface for HTTPAPIClient<K> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
@@ -146,6 +216,44 @@ impl<K: AuthenticationStore + Send + Sync> APIInterface for HTTPAPIClient<K> {
|
|||||||
let messages: Vec<Message> = self.request(&endpoint, Method::GET).await?;
|
let messages: Vec<Message> = self.request(&endpoint, Method::GET).await?;
|
||||||
Ok(messages)
|
Ok(messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn open_event_socket(&mut self) -> Result<WebsocketEventSocket, Self::Error> {
|
||||||
|
use tungstenite::http::StatusCode;
|
||||||
|
use tungstenite::handshake::client::Request as TungsteniteRequest;
|
||||||
|
use tungstenite::handshake::client::generate_key;
|
||||||
|
|
||||||
|
let uri = self.uri_for_endpoint("updates", Some(self.websocket_scheme()));
|
||||||
|
|
||||||
|
log::debug!("Connecting to websocket: {:?}", uri);
|
||||||
|
|
||||||
|
let auth = self.auth_store.get_token().await;
|
||||||
|
let host = uri.authority().unwrap().host();
|
||||||
|
let mut request = TungsteniteRequest::builder()
|
||||||
|
.header("Host", host)
|
||||||
|
.header("Connection", "Upgrade")
|
||||||
|
.header("Upgrade", "websocket")
|
||||||
|
.header("Sec-WebSocket-Version", "13")
|
||||||
|
.header("Sec-WebSocket-Key", generate_key())
|
||||||
|
.uri(uri.to_string())
|
||||||
|
.body(())
|
||||||
|
.expect("Unable to build websocket request");
|
||||||
|
|
||||||
|
log::debug!("Websocket request: {:?}", request);
|
||||||
|
|
||||||
|
if let Some(token) = &auth {
|
||||||
|
let header_value = token.to_header_value().to_str().unwrap().parse().unwrap(); // ugh
|
||||||
|
request.headers_mut().insert("Authorization", header_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (socket, response) = connect_async(request).await.unwrap();
|
||||||
|
log::debug!("Websocket connected: {:?}", response.status());
|
||||||
|
|
||||||
|
if response.status() != StatusCode::SWITCHING_PROTOCOLS {
|
||||||
|
return Err(Error::ClientError("Websocket connection failed".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(WebsocketEventSocket::new(socket))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: AuthenticationStore + Send + Sync> HTTPAPIClient<K> {
|
impl<K: AuthenticationStore + Send + Sync> HTTPAPIClient<K> {
|
||||||
@@ -157,15 +265,27 @@ impl<K: AuthenticationStore + Send + Sync> HTTPAPIClient<K> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uri_for_endpoint(&self, endpoint: &str) -> Uri {
|
fn uri_for_endpoint(&self, endpoint: &str, scheme: Option<&str>) -> Uri {
|
||||||
let mut parts = self.base_url.clone().into_parts();
|
let mut parts = self.base_url.clone().into_parts();
|
||||||
let root_path: PathBuf = parts.path_and_query.unwrap().path().into();
|
let root_path: PathBuf = parts.path_and_query.unwrap().path().into();
|
||||||
let path = root_path.join(endpoint);
|
let path = root_path.join(endpoint);
|
||||||
parts.path_and_query = Some(path.to_str().unwrap().parse().unwrap());
|
parts.path_and_query = Some(path.to_str().unwrap().parse().unwrap());
|
||||||
|
|
||||||
|
if let Some(scheme) = scheme {
|
||||||
|
parts.scheme = Some(scheme.parse().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
Uri::try_from(parts).unwrap()
|
Uri::try_from(parts).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn websocket_scheme(&self) -> &str {
|
||||||
|
if self.base_url.scheme().unwrap() == "https" {
|
||||||
|
"wss"
|
||||||
|
} else {
|
||||||
|
"ws"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn request<T: DeserializeOwned>(&mut self, endpoint: &str, method: Method) -> Result<T, Error> {
|
async fn request<T: DeserializeOwned>(&mut self, endpoint: &str, method: Method) -> Result<T, Error> {
|
||||||
self.request_with_body(endpoint, method, || { Body::empty() }).await
|
self.request_with_body(endpoint, method, || { Body::empty() }).await
|
||||||
}
|
}
|
||||||
@@ -188,7 +308,7 @@ impl<K: AuthenticationStore + Send + Sync> HTTPAPIClient<K> {
|
|||||||
{
|
{
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
|
|
||||||
let uri = self.uri_for_endpoint(endpoint);
|
let uri = self.uri_for_endpoint(endpoint, None);
|
||||||
log::debug!("Requesting {:?} {:?}", method, uri);
|
log::debug!("Requesting {:?} {:?}", method, uri);
|
||||||
|
|
||||||
let build_request = move |auth: &Option<JwtToken>| {
|
let build_request = move |auth: &Option<JwtToken>| {
|
||||||
@@ -320,4 +440,18 @@ mod test {
|
|||||||
let messages = client.get_messages(&conversation.guid, None, None, None).await.unwrap();
|
let messages = client.get_messages(&conversation.guid, None, None, None).await.unwrap();
|
||||||
assert!(!messages.is_empty());
|
assert!(!messages.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_updates() {
|
||||||
|
if !mock_client_is_reachable().await {
|
||||||
|
log::warn!("Skipping http_client tests (mock server not reachable)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut client = local_mock_client();
|
||||||
|
|
||||||
|
// We just want to see if the connection is established, we won't wait for any events
|
||||||
|
let _ = client.open_event_socket().await.unwrap();
|
||||||
|
assert!(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,18 @@ use async_trait::async_trait;
|
|||||||
pub use crate::model::{
|
pub use crate::model::{
|
||||||
Conversation, Message, ConversationID, MessageID,
|
Conversation, Message, ConversationID, MessageID,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod auth;
|
||||||
|
pub use crate::api::auth::{AuthenticationStore, InMemoryAuthenticationStore};
|
||||||
|
|
||||||
use crate::model::JwtToken;
|
use crate::model::JwtToken;
|
||||||
|
|
||||||
pub mod http_client;
|
pub mod http_client;
|
||||||
pub use http_client::HTTPAPIClient;
|
pub use http_client::HTTPAPIClient;
|
||||||
|
|
||||||
|
pub mod event_socket;
|
||||||
|
pub use event_socket::EventSocket;
|
||||||
|
|
||||||
use self::http_client::Credentials;
|
use self::http_client::Credentials;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -30,46 +37,7 @@ pub trait APIInterface {
|
|||||||
|
|
||||||
// (POST) /authenticate
|
// (POST) /authenticate
|
||||||
async fn authenticate(&mut self, credentials: Credentials) -> Result<JwtToken, Self::Error>;
|
async fn authenticate(&mut self, credentials: Credentials) -> Result<JwtToken, Self::Error>;
|
||||||
}
|
|
||||||
|
// (WS) /updates
|
||||||
#[async_trait]
|
async fn open_event_socket(&mut self) -> Result<impl EventSocket, Self::Error>;
|
||||||
pub trait AuthenticationStore {
|
|
||||||
async fn get_credentials(&mut self) -> Option<Credentials>;
|
|
||||||
async fn get_token(&mut self) -> Option<JwtToken>;
|
|
||||||
async fn set_token(&mut self, token: JwtToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InMemoryAuthenticationStore {
|
|
||||||
credentials: Option<Credentials>,
|
|
||||||
token: Option<JwtToken>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for InMemoryAuthenticationStore {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InMemoryAuthenticationStore {
|
|
||||||
pub fn new(credentials: Option<Credentials>) -> Self {
|
|
||||||
Self {
|
|
||||||
credentials,
|
|
||||||
token: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl AuthenticationStore for InMemoryAuthenticationStore {
|
|
||||||
async fn get_credentials(&mut self) -> Option<Credentials> {
|
|
||||||
self.credentials.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_token(&mut self) -> Option<JwtToken> {
|
|
||||||
self.token.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_token(&mut self, token: JwtToken) {
|
|
||||||
self.token = Some(token);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
17
kordophone/src/model/event.rs
Normal file
17
kordophone/src/model/event.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use crate::model::{Conversation, Message, UpdateItem};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Event {
|
||||||
|
ConversationChanged(Conversation),
|
||||||
|
MessageReceived(Conversation, Message),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UpdateItem> for Event {
|
||||||
|
fn from(update: UpdateItem) -> Self {
|
||||||
|
match update {
|
||||||
|
UpdateItem { conversation: Some(conversation), message: None, .. } => Event::ConversationChanged(conversation),
|
||||||
|
UpdateItem { conversation: Some(conversation), message: Some(message), .. } => Event::MessageReceived(conversation, message),
|
||||||
|
_ => panic!("Invalid update item: {:?}", update),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
pub mod conversation;
|
pub mod conversation;
|
||||||
|
pub mod event;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
|
pub mod update;
|
||||||
|
|
||||||
pub use conversation::Conversation;
|
pub use conversation::Conversation;
|
||||||
pub use conversation::ConversationID;
|
pub use conversation::ConversationID;
|
||||||
@@ -7,6 +9,10 @@ pub use conversation::ConversationID;
|
|||||||
pub use message::Message;
|
pub use message::Message;
|
||||||
pub use message::MessageID;
|
pub use message::MessageID;
|
||||||
|
|
||||||
|
pub use update::UpdateItem;
|
||||||
|
|
||||||
|
pub use event::Event;
|
||||||
|
|
||||||
pub mod jwt;
|
pub mod jwt;
|
||||||
pub use jwt::JwtToken;
|
pub use jwt::JwtToken;
|
||||||
|
|
||||||
|
|||||||
21
kordophone/src/model/update.rs
Normal file
21
kordophone/src/model/update.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use super::conversation::Conversation;
|
||||||
|
use super::message::Message;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct UpdateItem {
|
||||||
|
#[serde(rename = "messageSequenceNumber")]
|
||||||
|
pub seq: u64,
|
||||||
|
|
||||||
|
#[serde(rename = "conversation")]
|
||||||
|
pub conversation: Option<Conversation>,
|
||||||
|
|
||||||
|
#[serde(rename = "message")]
|
||||||
|
pub message: Option<Message>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for UpdateItem {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { seq: 0, conversation: None, message: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,9 +4,13 @@ use std::collections::HashMap;
|
|||||||
pub use crate::APIInterface;
|
pub use crate::APIInterface;
|
||||||
use crate::{
|
use crate::{
|
||||||
api::http_client::Credentials,
|
api::http_client::Credentials,
|
||||||
model::{Conversation, ConversationID, JwtToken, Message, MessageID}
|
model::{Conversation, ConversationID, JwtToken, Message, MessageID, UpdateItem, Event},
|
||||||
|
api::event_socket::EventSocket,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use futures_util::StreamExt;
|
||||||
|
use futures_util::stream::BoxStream;
|
||||||
|
|
||||||
pub struct TestClient {
|
pub struct TestClient {
|
||||||
pub version: &'static str,
|
pub version: &'static str,
|
||||||
pub conversations: Vec<Conversation>,
|
pub conversations: Vec<Conversation>,
|
||||||
@@ -28,6 +32,32 @@ impl TestClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct TestEventSocket {
|
||||||
|
pub events: Vec<Event>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestEventSocket {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { events: vec![] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl EventSocket for TestEventSocket {
|
||||||
|
type Error = TestError;
|
||||||
|
type EventStream = BoxStream<'static, Result<Event, TestError>>;
|
||||||
|
type UpdateStream = BoxStream<'static, Result<Vec<UpdateItem>, TestError>>;
|
||||||
|
|
||||||
|
async fn events(self) -> Self::EventStream {
|
||||||
|
futures_util::stream::iter(self.events.into_iter().map(Ok)).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn raw_updates(self) -> Self::UpdateStream {
|
||||||
|
let results: Vec<Result<Vec<UpdateItem>, TestError>> = vec![];
|
||||||
|
futures_util::stream::iter(results.into_iter()).boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl APIInterface for TestClient {
|
impl APIInterface for TestClient {
|
||||||
type Error = TestError;
|
type Error = TestError;
|
||||||
@@ -57,4 +87,10 @@ impl APIInterface for TestClient {
|
|||||||
|
|
||||||
Err(TestError::ConversationNotFound)
|
Err(TestError::ConversationNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn open_event_socket(&mut self) -> Result<impl EventSocket, Self::Error> {
|
||||||
|
Ok(TestEventSocket::new())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -54,9 +54,6 @@ impl DbusRepository for ServerImpl {
|
|||||||
fn get_conversations(&mut self) -> Result<Vec<arg::PropMap>, dbus::MethodErr> {
|
fn get_conversations(&mut self) -> Result<Vec<arg::PropMap>, dbus::MethodErr> {
|
||||||
self.send_event_sync(Event::GetAllConversations)
|
self.send_event_sync(Event::GetAllConversations)
|
||||||
.map(|conversations| {
|
.map(|conversations| {
|
||||||
// Convert conversations to DBus property maps
|
|
||||||
|
|
||||||
|
|
||||||
conversations.into_iter().map(|conv| {
|
conversations.into_iter().map(|conv| {
|
||||||
let mut map = arg::PropMap::new();
|
let mut map = arg::PropMap::new();
|
||||||
map.insert("guid".into(), arg::Variant(Box::new(conv.guid)));
|
map.insert("guid".into(), arg::Variant(Box::new(conv.guid)));
|
||||||
@@ -87,8 +84,6 @@ impl DbusRepository for ServerImpl {
|
|||||||
|
|
||||||
self.send_event_sync(|r| Event::GetMessages(conversation_id, last_message_id_opt, r))
|
self.send_event_sync(|r| Event::GetMessages(conversation_id, last_message_id_opt, r))
|
||||||
.map(|messages| {
|
.map(|messages| {
|
||||||
|
|
||||||
|
|
||||||
messages.into_iter().map(|msg| {
|
messages.into_iter().map(|msg| {
|
||||||
let mut map = arg::PropMap::new();
|
let mut map = arg::PropMap::new();
|
||||||
map.insert("id".into(), arg::Variant(Box::new(msg.id)));
|
map.insert("id".into(), arg::Variant(Box::new(msg.id)));
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ clap = { version = "4.5.20", features = ["derive"] }
|
|||||||
dbus = "0.9.7"
|
dbus = "0.9.7"
|
||||||
dbus-tree = "0.9.2"
|
dbus-tree = "0.9.2"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
|
env_logger = "0.11.8"
|
||||||
|
futures-util = "0.3.31"
|
||||||
kordophone = { path = "../kordophone" }
|
kordophone = { path = "../kordophone" }
|
||||||
kordophone-db = { path = "../kordophone-db" }
|
kordophone-db = { path = "../kordophone-db" }
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
|
|||||||
@@ -2,10 +2,14 @@ use kordophone::APIInterface;
|
|||||||
use kordophone::api::http_client::HTTPAPIClient;
|
use kordophone::api::http_client::HTTPAPIClient;
|
||||||
use kordophone::api::http_client::Credentials;
|
use kordophone::api::http_client::Credentials;
|
||||||
use kordophone::api::InMemoryAuthenticationStore;
|
use kordophone::api::InMemoryAuthenticationStore;
|
||||||
|
use kordophone::api::event_socket::EventSocket;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use crate::printers::{ConversationPrinter, MessagePrinter};
|
use crate::printers::{ConversationPrinter, MessagePrinter};
|
||||||
|
use kordophone::model::event::Event;
|
||||||
|
|
||||||
|
use futures_util::StreamExt;
|
||||||
|
|
||||||
pub fn make_api_client_from_env() -> HTTPAPIClient<InMemoryAuthenticationStore> {
|
pub fn make_api_client_from_env() -> HTTPAPIClient<InMemoryAuthenticationStore> {
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
@@ -37,6 +41,12 @@ pub enum Commands {
|
|||||||
|
|
||||||
/// Prints the server Kordophone version.
|
/// Prints the server Kordophone version.
|
||||||
Version,
|
Version,
|
||||||
|
|
||||||
|
/// Prints all events from the server.
|
||||||
|
Events,
|
||||||
|
|
||||||
|
/// Prints all raw updates from the server.
|
||||||
|
RawUpdates,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Commands {
|
impl Commands {
|
||||||
@@ -46,6 +56,8 @@ impl Commands {
|
|||||||
Commands::Version => client.print_version().await,
|
Commands::Version => client.print_version().await,
|
||||||
Commands::Conversations => client.print_conversations().await,
|
Commands::Conversations => client.print_conversations().await,
|
||||||
Commands::Messages { conversation_id } => client.print_messages(conversation_id).await,
|
Commands::Messages { conversation_id } => client.print_messages(conversation_id).await,
|
||||||
|
Commands::RawUpdates => client.print_raw_updates().await,
|
||||||
|
Commands::Events => client.print_events().await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,6 +94,35 @@ impl ClientCli {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn print_events(&mut self) -> Result<()> {
|
||||||
|
let socket = self.api.open_event_socket().await?;
|
||||||
|
|
||||||
|
let mut stream = socket.events().await;
|
||||||
|
while let Some(Ok(event)) = stream.next().await {
|
||||||
|
match event {
|
||||||
|
Event::ConversationChanged(conversation) => {
|
||||||
|
println!("Conversation changed: {}", conversation.guid);
|
||||||
|
}
|
||||||
|
Event::MessageReceived(conversation, message) => {
|
||||||
|
println!("Message received: msg: {} conversation: {}", message.guid, conversation.guid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn print_raw_updates(&mut self) -> Result<()> {
|
||||||
|
let socket = self.api.open_event_socket().await?;
|
||||||
|
|
||||||
|
println!("Listening for raw updates...");
|
||||||
|
let mut stream = socket.raw_updates().await;
|
||||||
|
while let Some(update) = stream.next().await {
|
||||||
|
println!("Got update: {:?}", update);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ mod daemon;
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
use log::LevelFilter;
|
||||||
|
|
||||||
/// A command line interface for the Kordophone library and daemon
|
/// A command line interface for the Kordophone library and daemon
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
@@ -43,8 +44,22 @@ async fn run_command(command: Commands) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn initialize_logging() {
|
||||||
|
// Weird: is this the best way to do this?
|
||||||
|
let log_level = std::env::var("RUST_LOG")
|
||||||
|
.map(|s| s.parse::<LevelFilter>().unwrap_or(LevelFilter::Info))
|
||||||
|
.unwrap_or(LevelFilter::Info);
|
||||||
|
|
||||||
|
env_logger::Builder::from_default_env()
|
||||||
|
.format_timestamp_secs()
|
||||||
|
.filter_level(log_level)
|
||||||
|
.init();
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
initialize_logging();
|
||||||
|
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
run_command(cli.command).await
|
run_command(cli.command).await
|
||||||
|
|||||||
Reference in New Issue
Block a user