commit 62788c1b26d824e15b1e4a7b333a71257ac7612b Author: JOLIMAITRE Matthieu Date: Mon Aug 22 01:59:56 2022 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ffcc601 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/db +/target \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f347bcd --- /dev/null +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..75cb270 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["harsh-client", "harsh-server", "harsh-common"] + diff --git a/harsh-client/.gitignore b/harsh-client/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/harsh-client/.gitignore @@ -0,0 +1 @@ +/target diff --git a/harsh-client/Cargo.toml b/harsh-client/Cargo.toml new file mode 100644 index 0000000..f9c1838 --- /dev/null +++ b/harsh-client/Cargo.toml @@ -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" } \ No newline at end of file diff --git a/harsh-client/src/main.rs b/harsh-client/src/main.rs new file mode 100644 index 0000000..9d03c4b --- /dev/null +++ b/harsh-client/src/main.rs @@ -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 { + 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::>(); + let content = rest.join(" "); + harsh_common::ClientRequest::new_ping(content) + } + _ => return None, + }; + + Some(Command::Request(command)) + } + + pub fn smart_split(input: &str) -> Vec { + 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::>(); + 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"); + } + } +} diff --git a/harsh-common/.gitignore b/harsh-common/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/harsh-common/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/harsh-common/Cargo.toml b/harsh-common/Cargo.toml new file mode 100644 index 0000000..c853431 --- /dev/null +++ b/harsh-common/Cargo.toml @@ -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" diff --git a/harsh-common/src/client.rs b/harsh-common/src/client.rs new file mode 100644 index 0000000..5bc5c0f --- /dev/null +++ b/harsh-common/src/client.rs @@ -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 { + 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 }, + } +} diff --git a/harsh-common/src/lib.rs b/harsh-common/src/lib.rs new file mode 100644 index 0000000..bd36c97 --- /dev/null +++ b/harsh-common/src/lib.rs @@ -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; diff --git a/harsh-common/src/server.rs b/harsh-common/src/server.rs new file mode 100644 index 0000000..6a7041b --- /dev/null +++ b/harsh-common/src/server.rs @@ -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 { + 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 }, + } +} diff --git a/harsh-server/.gitignore b/harsh-server/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/harsh-server/.gitignore @@ -0,0 +1 @@ +/target diff --git a/harsh-server/Cargo.toml b/harsh-server/Cargo.toml new file mode 100644 index 0000000..cd7b892 --- /dev/null +++ b/harsh-server/Cargo.toml @@ -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" diff --git a/harsh-server/src/gateway.rs b/harsh-server/src/gateway.rs new file mode 100644 index 0000000..49a7a0c --- /dev/null +++ b/harsh-server/src/gateway.rs @@ -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, + storage: Remote, +} + +impl GatewayProc { + pub fn new(client_handler: Remote, storage: Remote) -> 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(()) + } +} diff --git a/harsh-server/src/main.rs b/harsh-server/src/main.rs new file mode 100644 index 0000000..d292071 --- /dev/null +++ b/harsh-server/src/main.rs @@ -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; diff --git a/harsh-server/src/sessions.rs b/harsh-server/src/sessions.rs new file mode 100644 index 0000000..be8b354 --- /dev/null +++ b/harsh-server/src/sessions.rs @@ -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), + RemoveSession(Addr), + Send(Addr, String), +} + +#[derive(Debug, Default)] +pub struct SessionProc { + clients: HashMap)>, +} + +impl SessionProc { + fn add_client( + &mut self, + stream: TcpStream, + address: Addr, + remote: Remote, + ) { + 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) { + 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(); +} diff --git a/harsh-server/src/storage.rs b/harsh-server/src/storage.rs new file mode 100644 index 0000000..1ccc7ff --- /dev/null +++ b/harsh-server/src/storage.rs @@ -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), + ChannelDelete(Id), + ChannelGetAll(Sender>), + ChannelGetName(Id, Sender>), +} + +impl StorageCmd { + fn new_channel_create(name: impl ToString) -> (Self, Receiver) { + 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>) { + let (s, r) = oneshot::channel(); + (Self::ChannelGetAll(s), r) + } + fn new_channel_get_name(id: Id) -> (Self, Receiver>) { + let (s, r) = oneshot::channel(); + (Self::ChannelGetName(id, s), r) + } +} + +pub struct StorageProc { + base: Db, +} + +impl StorageProc { + pub fn new(path: S) -> Self + where + S: ToString, + { + let path = path.to_string(); + let base = sled::open(path).unwrap(); + Self { base } + } + + fn get(&self, path: S) -> Option + where + S: ToString, + T: SerDeser, + { + let path = path.to_string(); + T::read(&self.base, path) + } + fn set(&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 { + 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 { + let path = path.to_string(); + let len = path.len(); + db.scan_prefix(path) + .filter_map(move |result| -> Option { + 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::>(); + 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()); +} diff --git a/harsh-server/src/storage/models.rs b/harsh-server/src/storage/models.rs new file mode 100644 index 0000000..c576281 --- /dev/null +++ b/harsh-server/src/storage/models.rs @@ -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; + fn deser(input: &[u8]) -> Option; + fn read(db: &Db, path: String) -> Option; + fn write(&self, db: &Db, path: String); +} + +impl SerDeser for T +where + T: Serialize + DeserializeOwned, +{ + fn ser(&self) -> Vec { + serde_json::to_vec(self).unwrap() + } + + fn deser(input: &[u8]) -> Option { + serde_json::from_slice(input).ok() + } + + fn read(db: &Db, path: String) -> Option { + 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(); + } +} diff --git a/harsh-server/src/utils.rs b/harsh-server/src/utils.rs new file mode 100644 index 0000000..1e45be1 --- /dev/null +++ b/harsh-server/src/utils.rs @@ -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 { + 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()) +} diff --git a/start-client.sh b/start-client.sh new file mode 100755 index 0000000..3053c8a --- /dev/null +++ b/start-client.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cd harsh-client; +cargo run; diff --git a/start-server.sh b/start-server.sh new file mode 100755 index 0000000..5ceebc0 --- /dev/null +++ b/start-server.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cd harsh-server; +cargo run; diff --git a/watch-client.sh b/watch-client.sh new file mode 100755 index 0000000..cff9ea1 --- /dev/null +++ b/watch-client.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +nodemon "harsh-client/src" "harsh-server/src" "harsh-common/src" -x "./start-client.sh" -e "rs" diff --git a/watch-server.sh b/watch-server.sh new file mode 100644 index 0000000..7a82673 --- /dev/null +++ b/watch-server.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +nodemon "harsh-client/src" "harsh-server/src" "harsh-common/src" -x "./start-server.sh" -e "rs"