init
This commit is contained in:
commit
62788c1b26
23 changed files with 1532 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/db
|
||||
/target
|
710
Cargo.lock
generated
Normal file
710
Cargo.lock
generated
Normal file
|
@ -0,0 +1,710 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"once_cell",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fxhash"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "harsh-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"harsh_common",
|
||||
"sled",
|
||||
"telecomande",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "harsh_common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "harsh_server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"harsh_common",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sled",
|
||||
"telecomande",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad2bfd338099682614d3ee3fe0cd72e0b6a41ca6a87f6a74a3bd593c91650501"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sled"
|
||||
version = "0.34.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
"fs2",
|
||||
"fxhash",
|
||||
"libc",
|
||||
"log",
|
||||
"parking_lot 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "telecomande"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e2911385f0e73674cd9a881c3121b27a78bbc1a4f25fce42989e85c6b571679"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"parking_lot 0.12.1",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
3
Cargo.toml
Normal file
3
Cargo.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[workspace]
|
||||
members = ["harsh-client", "harsh-server", "harsh-common"]
|
||||
|
1
harsh-client/.gitignore
vendored
Normal file
1
harsh-client/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
12
harsh-client/Cargo.toml
Normal file
12
harsh-client/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "harsh-client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
sled = "0.34.7"
|
||||
telecomande = "1.2.2"
|
||||
tokio = { version = "1.20.1", features = ["full"] }
|
||||
harsh_common = { path = "../harsh-common" }
|
154
harsh-client/src/main.rs
Normal file
154
harsh-client/src/main.rs
Normal file
|
@ -0,0 +1,154 @@
|
|||
use tokio::{
|
||||
io::{stdin, AsyncBufReadExt, AsyncWriteExt, BufReader},
|
||||
net::TcpStream,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
println!("starting client ...");
|
||||
let stream = TcpStream::connect("localhost:8080").await.unwrap();
|
||||
println!("connected to 'localhost:8080'");
|
||||
let (reader, writer) = stream.into_split();
|
||||
tokio::spawn(async {
|
||||
let mut reader = BufReader::new(reader);
|
||||
loop {
|
||||
let mut line = String::new();
|
||||
reader.read_line(&mut line).await.unwrap();
|
||||
println!("received '{line}'");
|
||||
}
|
||||
});
|
||||
|
||||
let input_loop = tokio::spawn(async {
|
||||
let mut input = BufReader::new(stdin());
|
||||
let mut writer = writer;
|
||||
|
||||
loop {
|
||||
let mut line = String::new();
|
||||
input.read_line(&mut line).await.unwrap();
|
||||
let input = commands::parse(&line);
|
||||
match input {
|
||||
None => println!("failed to parse command"),
|
||||
Some(commands::Command::Help) => commands::help(),
|
||||
Some(commands::Command::Request(cmd)) => {
|
||||
println!("sending..");
|
||||
writer.write_all(cmd.serialize().as_bytes()).await.unwrap();
|
||||
writer.write_all(b"\n").await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
println!("awaiting input ...");
|
||||
input_loop.await.unwrap();
|
||||
}
|
||||
|
||||
mod commands {
|
||||
|
||||
pub enum Command {
|
||||
Help,
|
||||
Request(harsh_common::ClientRequest),
|
||||
}
|
||||
|
||||
pub fn parse(input: &str) -> Option<Command> {
|
||||
let mut parts = smart_split(input).into_iter();
|
||||
let command = match parts.next()?.as_str() {
|
||||
"help" => return Some(Command::Help),
|
||||
"ping" => {
|
||||
let rest = parts.collect::<Box<[_]>>();
|
||||
let content = rest.join(" ");
|
||||
harsh_common::ClientRequest::new_ping(content)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(Command::Request(command))
|
||||
}
|
||||
|
||||
pub fn smart_split(input: &str) -> Vec<String> {
|
||||
let input = input.trim();
|
||||
let mut result = Vec::new();
|
||||
|
||||
let mut capturing = false;
|
||||
let mut ignoring = false;
|
||||
let mut current = String::new();
|
||||
for char in input.chars() {
|
||||
let char: char = char;
|
||||
if ignoring {
|
||||
current.push(char);
|
||||
ignoring = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
match char {
|
||||
'\\' => {
|
||||
ignoring = true;
|
||||
}
|
||||
'"' => {
|
||||
capturing = !capturing;
|
||||
}
|
||||
' ' if !capturing => {
|
||||
result.push(current);
|
||||
current = String::new();
|
||||
}
|
||||
_ => current.push(char),
|
||||
}
|
||||
}
|
||||
result.push(current);
|
||||
result
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_smart_split() {
|
||||
assert_eq!(
|
||||
smart_split("hello world"),
|
||||
vec!["hello".to_string(), "world".to_string()]
|
||||
);
|
||||
assert_eq!(
|
||||
smart_split(r#""lorem ipsum" "dolor amit""#),
|
||||
vec!["lorem ipsum".to_string(), "dolor amit".to_string()]
|
||||
);
|
||||
assert_eq!(
|
||||
smart_split(r#"lorem "ipsum do"lor "amit""#),
|
||||
vec![
|
||||
"lorem".to_string(),
|
||||
"ipsum dolor".to_string(),
|
||||
"amit".to_string()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
pub struct Description {
|
||||
name: &'static str,
|
||||
params: &'static [&'static str],
|
||||
desc: &'static str,
|
||||
}
|
||||
|
||||
impl Description {
|
||||
pub const fn new(
|
||||
name: &'static str,
|
||||
params: &'static [&'static str],
|
||||
desc: &'static str,
|
||||
) -> Self {
|
||||
Self { name, desc, params }
|
||||
}
|
||||
|
||||
pub const ALL: &'static [Self] = &[
|
||||
// all commands
|
||||
Self::new("help", &[], "returns a help message"),
|
||||
Self::new(
|
||||
"ping",
|
||||
&["content"],
|
||||
"sends a ping with the specified content",
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
pub fn help() {
|
||||
for &Description { name, params, desc } in Description::ALL {
|
||||
let mut usage = params.iter().map(|s| s.to_string()).collect::<Vec<_>>();
|
||||
usage.insert(0, name.to_string());
|
||||
let usage = usage.join(" ");
|
||||
println!("{name}:\n\tusage:\n\t\t{usage}\n\n\tdescription:\n\t\t{desc}\n");
|
||||
}
|
||||
}
|
||||
}
|
2
harsh-common/.gitignore
vendored
Normal file
2
harsh-common/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
/Cargo.lock
|
10
harsh-common/Cargo.toml
Normal file
10
harsh-common/Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "harsh_common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.144", features = ["derive"] }
|
||||
serde_json = "1.0.83"
|
42
harsh-common/src/client.rs
Normal file
42
harsh-common/src/client.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
#[derive(Debug)]
|
||||
pub enum ClientRequest {
|
||||
Ping(Ping),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ping {
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl ClientRequest {
|
||||
pub fn new_ping(content: String) -> Self {
|
||||
Self::Ping(Ping { content })
|
||||
}
|
||||
|
||||
pub fn try_parse(line: &str) -> Option<Self> {
|
||||
let command: repr::Command = serde_json::from_str(line).ok()?;
|
||||
let mapped = match command {
|
||||
repr::Command::ping { content } => Self::Ping(Ping { content }),
|
||||
};
|
||||
Some(mapped)
|
||||
}
|
||||
|
||||
pub fn serialize(self) -> String {
|
||||
let mapped = match self {
|
||||
Self::Ping(Ping { content }) => repr::Command::ping { content },
|
||||
};
|
||||
serde_json::to_string(&mapped).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
mod repr {
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Command {
|
||||
ping { content: String },
|
||||
}
|
||||
}
|
14
harsh-common/src/lib.rs
Normal file
14
harsh-common/src/lib.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = 2 + 2;
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
|
||||
pub use client::{ClientRequest, Ping};
|
||||
mod client;
|
||||
|
||||
pub use server::{Pong, ServerRequest};
|
||||
mod server;
|
40
harsh-common/src/server.rs
Normal file
40
harsh-common/src/server.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
pub enum ServerRequest {
|
||||
Pong(Pong),
|
||||
}
|
||||
|
||||
pub struct Pong {
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl ServerRequest {
|
||||
pub fn new_pong(content: String) -> Self {
|
||||
Self::Pong(Pong { content })
|
||||
}
|
||||
|
||||
pub fn try_parse(line: &str) -> Option<Self> {
|
||||
let command: repr::Command = serde_json::from_str(line).ok()?;
|
||||
let mapped = match command {
|
||||
repr::Command::pong { content } => Self::Pong(Pong { content }),
|
||||
};
|
||||
Some(mapped)
|
||||
}
|
||||
|
||||
pub fn serialize(self) -> String {
|
||||
let mapped = match self {
|
||||
Self::Pong(Pong { content }) => repr::Command::pong { content },
|
||||
};
|
||||
serde_json::to_string(&mapped).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
mod repr {
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Command {
|
||||
pong { content: String },
|
||||
}
|
||||
}
|
1
harsh-server/.gitignore
vendored
Normal file
1
harsh-server/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
17
harsh-server/Cargo.toml
Normal file
17
harsh-server/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "harsh_server"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
#sleded = "1.0"
|
||||
sled = "0.34"
|
||||
telecomande = "1.2"
|
||||
tokio = { version = "1.20", features = ["full"] }
|
||||
harsh_common = { path = "../harsh-common/" }
|
||||
chrono = "0.4"
|
||||
rand = "0.8.5"
|
61
harsh-server/src/gateway.rs
Normal file
61
harsh-server/src/gateway.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use harsh_common::{Ping, Pong, ServerRequest};
|
||||
use telecomande::{Processor, Remote};
|
||||
|
||||
use harsh_common::ClientRequest;
|
||||
|
||||
use crate::{sessions, Addr, SessionProc, StorageProc};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GatewayCmd {
|
||||
Request(Addr, String),
|
||||
ClosedConnection(Addr),
|
||||
}
|
||||
|
||||
pub struct GatewayProc {
|
||||
client_handler: Remote<SessionProc>,
|
||||
storage: Remote<StorageProc>,
|
||||
}
|
||||
|
||||
impl GatewayProc {
|
||||
pub fn new(client_handler: Remote<SessionProc>, storage: Remote<StorageProc>) -> Self {
|
||||
Self {
|
||||
client_handler,
|
||||
storage,
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_request(&mut self, address: Addr, request: ClientRequest) {
|
||||
match request {
|
||||
ClientRequest::Ping(Ping { content }) => {
|
||||
println!("received ping! '{content:?}'");
|
||||
let response = ServerRequest::Pong(Pong { content });
|
||||
let content = response.serialize();
|
||||
self.client_handler
|
||||
.send(sessions::SessionCmd::Send(address, content))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[telecomande::async_trait]
|
||||
impl Processor for GatewayProc {
|
||||
type Command = GatewayCmd;
|
||||
type Error = ();
|
||||
async fn handle(&mut self, command: Self::Command) -> Result<(), Self::Error> {
|
||||
match command {
|
||||
GatewayCmd::Request(address, request) => {
|
||||
if let Some(request) = ClientRequest::try_parse(&request) {
|
||||
self.handle_request(address, request).await;
|
||||
} else {
|
||||
println!("failed to parse command");
|
||||
}
|
||||
}
|
||||
GatewayCmd::ClosedConnection(address) => self
|
||||
.client_handler
|
||||
.send(sessions::SessionCmd::RemoveSession(address))
|
||||
.unwrap(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
42
harsh-server/src/main.rs
Normal file
42
harsh-server/src/main.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use telecomande::{Executor, SimpleExecutor};
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
#[tokio::main]
|
||||
|
||||
async fn main() {
|
||||
println!("starting server ...");
|
||||
let client_handler = SimpleExecutor::new(SessionProc::default()).spawn();
|
||||
let storage = SimpleExecutor::new(StorageProc::new("./db")).spawn();
|
||||
let gateway =
|
||||
SimpleExecutor::new(GatewayProc::new(client_handler.remote(), storage.remote())).spawn();
|
||||
println!("spawned gateway");
|
||||
|
||||
let listener = TcpListener::bind("localhost:8080").await.unwrap();
|
||||
println!("listening on 'localhost:8080' ...");
|
||||
|
||||
let client_handler = client_handler.remote();
|
||||
loop {
|
||||
let (stream, address) = listener.accept().await.unwrap();
|
||||
println!("new connection from '{address:?}'");
|
||||
|
||||
client_handler
|
||||
.send(sessions::SessionCmd::AddSession(
|
||||
stream,
|
||||
address,
|
||||
gateway.remote(),
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
mod utils;
|
||||
pub use utils::{Addr, Id};
|
||||
|
||||
mod gateway;
|
||||
pub use gateway::{GatewayCmd, GatewayProc};
|
||||
|
||||
mod sessions;
|
||||
pub use sessions::{SessionCmd, SessionProc};
|
||||
|
||||
pub use storage::{StorageCmd, StorageProc};
|
||||
mod storage;
|
82
harsh-server/src/sessions.rs
Normal file
82
harsh-server/src/sessions.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use std::{collections::HashMap, net::SocketAddr};
|
||||
|
||||
use telecomande::{Processor, Remote};
|
||||
use tokio::{
|
||||
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
|
||||
net::{
|
||||
tcp::{OwnedReadHalf, OwnedWriteHalf},
|
||||
TcpStream,
|
||||
},
|
||||
task::JoinHandle,
|
||||
};
|
||||
|
||||
use crate::{gateway, Addr};
|
||||
#[derive(Debug)]
|
||||
pub enum SessionCmd {
|
||||
AddSession(TcpStream, SocketAddr, Remote<gateway::GatewayProc>),
|
||||
RemoveSession(Addr),
|
||||
Send(Addr, String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SessionProc {
|
||||
clients: HashMap<Addr, (OwnedWriteHalf, JoinHandle<()>)>,
|
||||
}
|
||||
|
||||
impl SessionProc {
|
||||
fn add_client(
|
||||
&mut self,
|
||||
stream: TcpStream,
|
||||
address: Addr,
|
||||
remote: Remote<gateway::GatewayProc>,
|
||||
) {
|
||||
let (reader, writer) = stream.into_split();
|
||||
let handle = tokio::spawn(session(address.clone(), reader, remote));
|
||||
self.clients.insert(address, (writer, handle));
|
||||
}
|
||||
}
|
||||
|
||||
#[telecomande::async_trait]
|
||||
impl Processor for SessionProc {
|
||||
type Command = SessionCmd;
|
||||
|
||||
type Error = ();
|
||||
|
||||
async fn handle(&mut self, command: Self::Command) -> Result<(), Self::Error> {
|
||||
match command {
|
||||
SessionCmd::AddSession(stream, address, remote) => {
|
||||
let address = Addr::new(address);
|
||||
self.add_client(stream, address, remote)
|
||||
}
|
||||
SessionCmd::RemoveSession(address) => {
|
||||
self.clients.remove(&address);
|
||||
}
|
||||
SessionCmd::Send(address, content) => {
|
||||
if let Some((client, _)) = self.clients.get_mut(&address) {
|
||||
client.write_all(content.as_bytes()).await.unwrap();
|
||||
client.write_all(b"\n").await.unwrap();
|
||||
} else {
|
||||
println!("failed to find session with address '{address:?}'")
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn session(address: Addr, reader: OwnedReadHalf, remote: Remote<gateway::GatewayProc>) {
|
||||
let mut reader = BufReader::new(reader);
|
||||
loop {
|
||||
let mut line = String::new();
|
||||
if let Err(error) = reader.read_line(&mut line).await {
|
||||
eprintln!("{error}");
|
||||
break;
|
||||
}
|
||||
remote
|
||||
.send(gateway::GatewayCmd::Request(address.clone(), line.clone()))
|
||||
.unwrap();
|
||||
}
|
||||
remote
|
||||
.send(gateway::GatewayCmd::ClosedConnection(address))
|
||||
.unwrap();
|
||||
}
|
202
harsh-server/src/storage.rs
Normal file
202
harsh-server/src/storage.rs
Normal file
|
@ -0,0 +1,202 @@
|
|||
use sled::Db;
|
||||
use telecomande::Processor;
|
||||
use tokio::sync::oneshot::{self, Receiver, Sender};
|
||||
|
||||
use crate::Id;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum StorageCmd {
|
||||
ChannelCreate(String, Sender<Id>),
|
||||
ChannelDelete(Id),
|
||||
ChannelGetAll(Sender<Vec<Id>>),
|
||||
ChannelGetName(Id, Sender<Option<String>>),
|
||||
}
|
||||
|
||||
impl StorageCmd {
|
||||
fn new_channel_create(name: impl ToString) -> (Self, Receiver<Id>) {
|
||||
let (s, r) = oneshot::channel();
|
||||
(Self::ChannelCreate(name.to_string(), s), r)
|
||||
}
|
||||
fn new_channel_delete(id: Id) -> Self {
|
||||
Self::ChannelDelete(id)
|
||||
}
|
||||
fn new_channel_get_all() -> (Self, Receiver<Vec<Id>>) {
|
||||
let (s, r) = oneshot::channel();
|
||||
(Self::ChannelGetAll(s), r)
|
||||
}
|
||||
fn new_channel_get_name(id: Id) -> (Self, Receiver<Option<String>>) {
|
||||
let (s, r) = oneshot::channel();
|
||||
(Self::ChannelGetName(id, s), r)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StorageProc {
|
||||
base: Db,
|
||||
}
|
||||
|
||||
impl StorageProc {
|
||||
pub fn new<S>(path: S) -> Self
|
||||
where
|
||||
S: ToString,
|
||||
{
|
||||
let path = path.to_string();
|
||||
let base = sled::open(path).unwrap();
|
||||
Self { base }
|
||||
}
|
||||
|
||||
fn get<S, T>(&self, path: S) -> Option<T>
|
||||
where
|
||||
S: ToString,
|
||||
T: SerDeser,
|
||||
{
|
||||
let path = path.to_string();
|
||||
T::read(&self.base, path)
|
||||
}
|
||||
fn set<S, T>(&self, path: S, item: T)
|
||||
where
|
||||
S: ToString,
|
||||
T: SerDeser,
|
||||
{
|
||||
let path = path.to_string();
|
||||
item.write(&self.base, path)
|
||||
}
|
||||
|
||||
fn list(&self, path: impl ToString) -> Vec<Id> {
|
||||
let path = path.to_string();
|
||||
list(&self.base, path).collect() // TODO: turn into iterator with limits
|
||||
}
|
||||
|
||||
// firsts (x)
|
||||
// lasts (x)
|
||||
// from (id, x)
|
||||
// to (id, x)
|
||||
|
||||
fn remove(&self, path: impl ToString) {
|
||||
let path = path.to_string();
|
||||
remove(&self.base, path)
|
||||
}
|
||||
}
|
||||
|
||||
#[telecomande::async_trait]
|
||||
impl Processor for StorageProc {
|
||||
type Command = StorageCmd;
|
||||
|
||||
type Error = ();
|
||||
|
||||
async fn handle(&mut self, command: Self::Command) -> Result<(), Self::Error> {
|
||||
match command {
|
||||
// channels
|
||||
StorageCmd::ChannelDelete(id) => self.remove(format!("/channels/{id}")),
|
||||
StorageCmd::ChannelCreate(name, sender) => {
|
||||
let item = Channel::new(name);
|
||||
let id = item.get_id();
|
||||
self.set(format!("/channels/{id}"), item);
|
||||
sender.send(id).unwrap();
|
||||
}
|
||||
StorageCmd::ChannelGetAll(sender) => {
|
||||
let results = self.list("/channels/");
|
||||
sender.send(results).unwrap();
|
||||
}
|
||||
StorageCmd::ChannelGetName(id, sender) => {
|
||||
let result = self
|
||||
.get::<_, Channel>(format!("/channels/{id}"))
|
||||
.map(|channel| channel.get_name().to_string());
|
||||
sender.send(result).unwrap();
|
||||
} //
|
||||
// ChannelGetParent
|
||||
|
||||
// messages
|
||||
// c
|
||||
// d
|
||||
// l
|
||||
// gcontent
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod models;
|
||||
pub use models::{Channel, Msg, SerDeser, User};
|
||||
|
||||
fn list(db: &Db, path: impl ToString) -> impl Iterator<Item = Id> {
|
||||
let path = path.to_string();
|
||||
let len = path.len();
|
||||
db.scan_prefix(path)
|
||||
.filter_map(move |result| -> Option<Id> {
|
||||
let (key, _) = result.ok()?;
|
||||
let string = String::from_utf8(key.iter().cloned().collect()).unwrap();
|
||||
let suffix = &string[len..];
|
||||
Id::from_string(suffix)
|
||||
})
|
||||
}
|
||||
|
||||
fn remove(db: &Db, path: impl ToString) {
|
||||
let path = path.to_string();
|
||||
db.remove(path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list() {
|
||||
let db = sled::open("/tmp/test-db").unwrap();
|
||||
db.insert("/some/path/123", b"hello1").unwrap();
|
||||
db.insert("/some/path/1234", b"hello2").unwrap();
|
||||
db.insert("/some/path/12345", b"hello3").unwrap();
|
||||
let results = list(&db, "/some/path/".to_string());
|
||||
let vec = results.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
vec,
|
||||
vec![
|
||||
Id::from_string("123").unwrap(),
|
||||
Id::from_string("1234").unwrap(),
|
||||
Id::from_string("12345").unwrap()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_channels() {
|
||||
use telecomande::{Executor, SimpleExecutor};
|
||||
// cleaning;
|
||||
std::fs::remove_dir_all("/tmp/db-test").ok();
|
||||
|
||||
// instantiation
|
||||
let store = SimpleExecutor::new(StorageProc::new("/tmp/db-test")).spawn();
|
||||
let remote = store.remote();
|
||||
|
||||
// insertion
|
||||
let (cmd, rec) = StorageCmd::new_channel_create("a-channel");
|
||||
remote.send(cmd).unwrap();
|
||||
let id = rec.await.unwrap();
|
||||
|
||||
// query all
|
||||
let (cmd, rec) = StorageCmd::new_channel_get_all();
|
||||
remote.send(cmd).unwrap();
|
||||
let result = rec.await.unwrap();
|
||||
assert_eq!(result.len(), 1);
|
||||
let first = result[0];
|
||||
assert_eq!(first, id);
|
||||
|
||||
// query property
|
||||
let (cmd, rec) = StorageCmd::new_channel_get_name(id);
|
||||
remote.send(cmd).unwrap();
|
||||
let result = rec.await.unwrap();
|
||||
assert_eq!(result.unwrap(), "a-channel".to_string());
|
||||
|
||||
// insertion
|
||||
let (cmd, rec) = StorageCmd::new_channel_create("b-channel");
|
||||
remote.send(cmd).unwrap();
|
||||
let id2 = rec.await.unwrap();
|
||||
|
||||
// query all
|
||||
let (cmd, rec) = StorageCmd::new_channel_get_all();
|
||||
remote.send(cmd).unwrap();
|
||||
let result = rec.await.unwrap();
|
||||
assert_eq!(result.len(), 2);
|
||||
|
||||
// query property
|
||||
let (cmd, rec) = StorageCmd::new_channel_get_name(id2);
|
||||
remote.send(cmd).unwrap();
|
||||
let result = rec.await.unwrap();
|
||||
assert_eq!(result.unwrap(), "b-channel".to_string());
|
||||
}
|
67
harsh-server/src/storage/models.rs
Normal file
67
harsh-server/src/storage/models.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use sled::Db;
|
||||
|
||||
use crate::Id;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Channel {
|
||||
id: Id,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
pub fn new(name: String) -> Self {
|
||||
let id = Id::from_now();
|
||||
Self { id, name }
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> Id {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct User {
|
||||
id: Id,
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Msg {
|
||||
id: Id,
|
||||
content: String,
|
||||
}
|
||||
|
||||
pub trait SerDeser: Serialize + DeserializeOwned {
|
||||
fn ser(&self) -> Vec<u8>;
|
||||
fn deser(input: &[u8]) -> Option<Self>;
|
||||
fn read(db: &Db, path: String) -> Option<Self>;
|
||||
fn write(&self, db: &Db, path: String);
|
||||
}
|
||||
|
||||
impl<T> SerDeser for T
|
||||
where
|
||||
T: Serialize + DeserializeOwned,
|
||||
{
|
||||
fn ser(&self) -> Vec<u8> {
|
||||
serde_json::to_vec(self).unwrap()
|
||||
}
|
||||
|
||||
fn deser(input: &[u8]) -> Option<Self> {
|
||||
serde_json::from_slice(input).ok()
|
||||
}
|
||||
|
||||
fn read(db: &Db, path: String) -> Option<Self> {
|
||||
let bytes = db.get(path).unwrap()?;
|
||||
Self::deser(&bytes)
|
||||
}
|
||||
|
||||
fn write(&self, db: &Db, path: String) {
|
||||
let bytes = self.ser();
|
||||
db.insert(path, bytes).unwrap();
|
||||
}
|
||||
}
|
56
harsh-server/src/utils.rs
Normal file
56
harsh-server/src/utils.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use std::{fmt::Display, net::SocketAddr};
|
||||
|
||||
use rand::random;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Addr(String);
|
||||
|
||||
impl Addr {
|
||||
pub fn new(address: SocketAddr) -> Self {
|
||||
let string = format!("{address:?}");
|
||||
Self(string)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Id(u64);
|
||||
|
||||
impl Id {
|
||||
pub fn from_now() -> Self {
|
||||
let ms = chrono::Utc::now().timestamp_millis() as u64;
|
||||
let total = (ms * 1000) + rand_range(1000);
|
||||
Self(total)
|
||||
}
|
||||
|
||||
pub fn from_string(input: &str) -> Option<Self> {
|
||||
let inner: u64 = input.parse().ok()?;
|
||||
Some(Self(inner))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_convertion() {
|
||||
let id = Id::from_now();
|
||||
let str = id.to_string();
|
||||
assert_eq!(id, Id::from_string(&str).unwrap());
|
||||
}
|
||||
|
||||
fn rand_range(n: u64) -> u64 {
|
||||
let random: u64 = random();
|
||||
random % n
|
||||
}
|
||||
|
||||
impl Display for Id {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let inner = self.0;
|
||||
let padded = format!("{inner:0>20}"); // pads to the left to make 20 chars of length
|
||||
f.write_str(&padded)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn length_of_max() {
|
||||
assert_eq!(u64::MAX, 18446744073709551615_u64);
|
||||
assert_eq!(20, "18446744073709551615".len())
|
||||
}
|
4
start-client.sh
Executable file
4
start-client.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
|
||||
cd harsh-client;
|
||||
cargo run;
|
4
start-server.sh
Executable file
4
start-server.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
|
||||
cd harsh-server;
|
||||
cargo run;
|
3
watch-client.sh
Executable file
3
watch-client.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
nodemon "harsh-client/src" "harsh-server/src" "harsh-common/src" -x "./start-client.sh" -e "rs"
|
3
watch-server.sh
Normal file
3
watch-server.sh
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
nodemon "harsh-client/src" "harsh-server/src" "harsh-common/src" -x "./start-server.sh" -e "rs"
|
Loading…
Add table
Add a link
Reference in a new issue