From c9de11ff61d9ba1d38a415c8cfe6ccb0866e14ab Mon Sep 17 00:00:00 2001 From: Jeff Baskin Date: Wed, 26 Mar 2025 17:42:34 -0400 Subject: [PATCH] Abandoned path. Senders can only be moved oncde. --- Cargo.lock | 159 +++++++----- Cargo.toml | 3 + src/backend.rs | 42 +++- src/client.rs | 89 +++++++ src/docbuilder.rs | 135 +++++++++++ src/field.rs | 122 ++++++++++ src/frontend.rs | 31 ++- src/lib.rs | 571 ++------------------------------------------ src/main.rs | 77 ++++-- src/old-lib.rs | 597 ++++++++++++++++++++++++++++++++++++++++++++++ src/old-queue.rs | 156 ++++++++++++ src/queue.rs | 258 +++++++++++++++++--- 12 files changed, 1564 insertions(+), 676 deletions(-) create mode 100644 src/docbuilder.rs create mode 100644 src/field.rs create mode 100644 src/old-lib.rs create mode 100644 src/old-queue.rs diff --git a/Cargo.lock b/Cargo.lock index 296ca8d..e5b98b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,9 +69,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.85" +version = "0.1.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" +checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" dependencies = [ "proc-macro2", "quote", @@ -186,9 +186,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "block-buffer" @@ -207,9 +207,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cfg-if" @@ -219,9 +219,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.27" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", "clap_derive", @@ -229,9 +229,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.27" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" dependencies = [ "anstream", "anstyle", @@ -241,9 +241,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck", "proc-macro2", @@ -399,7 +399,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets", ] [[package]] @@ -459,9 +471,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -471,9 +483,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", @@ -512,15 +524,15 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "lock_api" @@ -534,9 +546,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "matchit" @@ -558,9 +570,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] @@ -572,7 +584,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -585,6 +597,7 @@ dependencies = [ "clap", "rand", "tokio", + "tower", "uuid", ] @@ -622,9 +635,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "parking_lot" @@ -684,18 +697,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" dependencies = [ "proc-macro2", ] @@ -727,14 +740,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ "bitflags", ] @@ -747,15 +760,15 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "scopeguard" @@ -765,18 +778,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", @@ -785,9 +798,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.137" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -797,9 +810,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" dependencies = [ "itoa", "serde", @@ -839,9 +852,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" @@ -873,9 +886,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.96" +version = "2.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" dependencies = [ "proc-macro2", "quote", @@ -890,9 +903,9 @@ checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" [[package]] name = "time" -version = "0.3.37" +version = "0.3.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "bb041120f25f8fbe8fd2dbe4671c7c2ed74d83be2e7a77529bf7e0790ae3f472" dependencies = [ "deranged", "itoa", @@ -905,15 +918,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" dependencies = [ "num-conv", "time-core", @@ -998,15 +1011,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "utf8parse" @@ -1016,11 +1029,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.12.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" +checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" dependencies = [ - "getrandom", + "getrandom 0.3.1", ] [[package]] @@ -1035,6 +1048,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -1117,6 +1139,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index b43f5cf..31be24e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,6 @@ clap = { version = "4.5.1", features = ["derive"] } rand = "0.8.5" tokio = { version = "1.36.0", features = ["full"] } uuid = { version = "1.8.0", features = ["v4"] } + +[dev-dependencies] +tower = { version = "0.5.2", features = ["util"] } diff --git a/src/backend.rs b/src/backend.rs index a5460d6..099c803 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -4,6 +4,12 @@ use std::{ thread::spawn, }; +#[derive(Clone)] +pub enum BackEndMessage { + Get, + Response(String), +} + struct BackEnd { tx: Sender, rx: Receiver, @@ -11,10 +17,7 @@ struct BackEnd { impl BackEnd { fn new(tx: Sender, rx: Receiver) -> Self { - Self { - tx: tx, - rx: rx, - } + Self { tx: tx, rx: rx } } fn start(queue_tx: Sender) { @@ -29,22 +32,49 @@ impl BackEnd { fn listen(&self) { loop { self.rx.recv().unwrap(); + self.tx + .send(BackEndMessage::Response("Something goes here".to_string()).into()) + .unwrap(); } } } #[cfg(test)] mod backends { - use std::time::Duration; use super::*; + use std::time::Duration; + + fn run_service() -> (Sender, Receiver) { + let (tx, rx) = channel(); + let service_tx: Sender; + BackEnd::start(tx); + match rx.recv().unwrap() { + Message::Register(result) => service_tx = result, + _ => unreachable!("should register the service"), + } + (service_tx, rx) + } #[test] fn get_registered() { let (tx, rx) = channel(); BackEnd::start(tx); match rx.recv_timeout(Duration::from_millis(500)).unwrap() { - Message::Register(_) => {}, + Message::Register(_) => {} _ => unreachable!("should register the service"), } } + + #[test] + fn request_home_page() { + let (tx, rx) = run_service(); + tx.send(BackEndMessage::Get.into()).unwrap(); + match rx.recv_timeout(Duration::from_millis(500)).unwrap() { + Message::BEMsg(result) => match result { + BackEndMessage::Response(_) => {} + _ => unreachable!("wrong message type"), + }, + _ => unreachable!("wrong message type"), + } + } } diff --git a/src/client.rs b/src/client.rs index be783d9..13d6c4d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,3 +1,91 @@ +use crate::{ + queue::{Message, MessageData}, + ClientMessage, +}; +use std::{ + sync::mpsc::{channel, Receiver, Sender}, + thread::spawn, +}; + +pub struct Reply; + +impl Reply { + pub fn get_session(&self) -> String { + "id".to_string() + } + + pub fn get_content(&self) -> String { + "Something goes here.".to_string() + } +} + +struct Client { + tx: Sender, + rx: Receiver +} + +impl Client { + fn new(tx: Sender, rx: Receiver) -> Self { + Self { + tx: tx, + rx: rx, + } + } + + fn start(queue_tx: Sender) { + spawn(move || { + let (tx, rx) = channel(); + let service = Self::new(queue_tx.clone(), rx); + queue_tx.send(Message::new(tx.into())).unwrap(); + service.listen(); + }); + } + + fn listen(&self) { + loop { + let msg = self.rx.recv().unwrap(); + match msg.get_data() { + MessageData::ClientMsg(output) => { + match output { + ClientMessage::Req(data) => { + let result = crate::client::Reply {}; + data.tx.send(result).unwrap(); + }, + } + }, + _ => {}, + } + } + } +} + +#[cfg(test)] +mod clients { + use crate::create_requests::root_doc_no_sess; + use super::*; + + fn setup_client() -> (Sender, Receiver) { + let (tx, rx) = channel(); + Client::start(tx); + let client_tx = match rx.recv().unwrap().get_data() { + MessageData::Register(result) => result.clone(), + _ => unreachable!("Register service first"), + }; + (client_tx, rx) + } + + #[test] + fn new_root_doc_request() { + let (tx, rx) = setup_client(); + let (req, client_rx) = root_doc_no_sess(); + tx.send(Message::new(req.into())).unwrap(); + client_rx.recv().unwrap(); + } +} + + + +/* use super::{Message, Msg, MsgData, SessionMsg}; use std::{ collections::HashMap, @@ -155,3 +243,4 @@ mod messages { } } } +*/ diff --git a/src/docbuilder.rs b/src/docbuilder.rs new file mode 100644 index 0000000..b1eaf54 --- /dev/null +++ b/src/docbuilder.rs @@ -0,0 +1,135 @@ +use crate::{ + field::Field, + queue::{Message, QueueClient}, +}; +use std::sync::mpsc::{Receiver, Sender}; + +#[derive(Debug)] +pub struct Document { + session: Field, + content: Field, +} + +impl Document { + pub fn new(session: Field, content: Field) -> Self { + Self { + session: session, + content: content, + } + } + + /// Get the session id information. + pub fn get_session(&self) -> String { + self.session.to_string() + } + + /// Get the requested content. + pub fn get_content(&self) -> String { + self.content.to_string() + } +} + +#[cfg(test)] +mod documeents { + use super::*; + + #[test] + fn create_response() { + let id = "something"; + let txt = "stuff"; + let res = Document::new(id.into(), txt.into()); + match res.session { + Field::Static(data) => assert_eq!(data, id), + _ => unreachable!("wrong field type"), + } + match res.content { + Field::Static(data) => assert_eq!(data, txt), + _ => unreachable!("wrong field type"), + } + } + + #[test] + fn get_id_string() { + let id = "id"; + let doc = "content"; + let reply = Document::new(id.into(), doc.into()); + assert_eq!(reply.get_session(), id); + assert_eq!(reply.get_content(), doc); + } + + #[test] + fn get_id_content() { + let id = "session"; + let doc = "stuff"; + let reply = Document::new(id.into(), doc.into()); + assert_eq!(reply.get_session(), id); + assert_eq!(reply.get_content(), doc); + } +} + +pub struct DocumentBuilder { + _tx: Sender, + rx: Receiver, +} + +impl QueueClient for DocumentBuilder { + fn new(tx: Sender, rx: Receiver) -> Self { + Self { + _tx: tx, + rx: rx, + } + } + + fn get_receiver(&self) -> &Receiver { + &self.rx + } + + fn listen(&self) { + let rx = self.get_receiver(); + loop { + match rx.recv().unwrap() { + Message::Req(data) => { + //data.get_sender().send(Document::new("id".into(), "stuff".into())).unwrap(); + let doc = Document::new("id".into(), "stuff".into()); + data.send(doc); + }, + _ => {}, + } + } + } +} + +#[cfg(test)] +mod builders { + use crate::request_test_data::request_root_document; + use std::sync::mpsc::{channel, TryRecvError}; + use super::*; + + fn run_service() -> (Sender, Receiver) { + let (tx, rx) = channel(); + let service_tx: Sender; + DocumentBuilder::start(tx); + match rx.recv().unwrap() { + Message::Register(result) => service_tx = result, + _ => unreachable!("should register the service"), + } + (service_tx, rx) + } + + #[test] + fn get_document() { + let (tx, rx) = run_service(); + let (msg, msg_rx) = request_root_document(); + tx.send(msg.into()).unwrap(); + match rx.try_recv() { + Ok(_) => unreachable!("nothing should go back to the queue"), + Err(err) => { + match err { + TryRecvError::Empty => {}, + _ => unreachable!("channel should still be open"), + } + }, + } + msg_rx.recv().unwrap(); + } +} diff --git a/src/field.rs b/src/field.rs new file mode 100644 index 0000000..c453886 --- /dev/null +++ b/src/field.rs @@ -0,0 +1,122 @@ +use std::fmt; +use uuid::Uuid; + +#[derive(Clone, Debug)] +pub enum Field { + Static(String), + Uuid(Uuid), +} + +impl From for Field { + fn from(value: String) -> Self { + match Uuid::try_from(value.as_str()) { + Ok(data) => return Field::Uuid(data), + Err(_) => {} + } + Field::Static(value) + } +} + +impl From<&str> for Field { + fn from(value: &str) -> Self { + match Uuid::try_from(value) { + Ok(data) => return Field::Uuid(data), + Err(_) => {} + } + Field::Static(value.into()) + } +} + +impl From for Field { + fn from(value: Uuid) -> Self { + Field::Uuid(value) + } +} + +impl fmt::Display for Field { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Field::Uuid(data) => write!(f, "{}", data), + Field::Static(data) => write!(f, "{}", data), + } + } +} + +#[cfg(test)] +mod fields { + use super::*; + + #[test] + fn string_to_field() { + let entries = ["test1".to_string(), "test2".to_string()]; + for data in entries { + match data.clone().into() { + Field::Static(result) => assert_eq!(result, data), + _ => unreachable!("shouuld have been a static field"), + } + } + } + + #[test] + fn str_to_field() { + let entries = ["test1", "test2"]; + for data in entries { + match data.into() { + Field::Static(result) => assert_eq!(result, data), + _ => unreachable!("shouuld have been a static field"), + } + } + } + + #[test] + fn uuid_to_field() { + let id = Uuid::new_v4(); + match id.into() { + Field::Uuid(result) => assert_eq!(result, id), + _ => unreachable!("should have been a uuid field"), + } + } + + #[test] + fn uuid_string_to_field() { + let id = Uuid::new_v4(); + let id_string = id.to_string(); + match id_string.into() { + Field::Uuid(result) => assert_eq!(result, id), + _ => unreachable!("should have been a uuid field"), + } + } + + #[test] + fn uuid_str_to_field() { + let id = Uuid::new_v4(); + let id_string = id.to_string(); + let id_str = id_string.as_str(); + match id_str.into() { + Field::Uuid(result) => assert_eq!(result, id), + _ => unreachable!("should have been a uuid field"), + } + } + + #[test] + fn uuid_field_to_string() { + let id = Uuid::new_v4(); + let result = id.to_string(); + let input = Field::Uuid(id); + assert_eq!(input.to_string(), result); + } + + #[test] + fn str_field_to_string() { + let result = "Something goes here"; + let input: Field = result.into(); + assert_eq!(input.to_string(), result); + } + + #[test] + fn string_field_to_string() { + let result = "Another string".to_string(); + let input: Field = result.clone().into(); + assert_eq!(input.to_string(), result); + } +} diff --git a/src/frontend.rs b/src/frontend.rs index 031375c..98b3ba4 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -1,4 +1,4 @@ -use crate::{queue::Message, session2::SessionMessage, Field, Request}; +use crate::{backend::BackEndMessage, queue::Message, session2::SessionMessage, Field, Request}; use std::{ sync::mpsc::{channel, Receiver, Sender}, thread::spawn, @@ -25,12 +25,31 @@ impl FrontEnd { fn listen(&self) { loop { - self.rx.recv().unwrap(); + match self.rx.recv().unwrap() { + Message::Req(output) => self.validate_session(output), + Message::SessMsg(output) => match output { + SessionMessage::New(data) => { + } + _ => {} + }, + _ => {} + } + let id: Option = None; let msg: SessionMessage = id.into(); self.tx.send(msg.into()).unwrap(); } } + + fn forward_request(&self, sess_id: Field) { + self.tx.send(BackEndMessage::Get.into()).unwrap(); + } + + fn validate_session(&self, req: Request) { + let id: Option = None; + let msg: SessionMessage = id.into(); + self.tx.send(msg.into()).unwrap(); + } } #[cfg(test)] @@ -76,6 +95,12 @@ mod frontends { } let (new_sess, sess_id) = create_session_new(); tx.send(new_sess.into()).unwrap(); - // generate backend request. + match rx.recv_timeout(Duration::from_millis(500)).unwrap() { + Message::BEMsg(result) => match result { + BackEndMessage::Get => {} + _ => unreachable!("incorrect message type"), + }, + _ => unreachable!("incorrect message type"), + } } } diff --git a/src/lib.rs b/src/lib.rs index 9c591c0..a3f9a32 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,572 +1,31 @@ -mod backend; -mod client; -mod data; -mod error; -mod frontend; -mod message; -mod queue; -mod router; -mod session; -mod session2; +mod field; -use client::{Client, ClientMsg}; -use message::{Message, MsgData}; -use router::Router; -use session::{Session, SessionData, SessionMsg}; -use std::{ - collections::HashMap, - ops::Deref, - sync::mpsc::{channel, Receiver, Sender}, -}; -use uuid::Uuid; +use field::Field; -#[derive(Clone)] -enum Field { - Static(String), - Uuid(Uuid), -} +pub struct Reply; -impl From for Field { - fn from(value: String) -> Self { - match Uuid::try_from(value.as_str()) { - Ok(data) => return Field::Uuid(data), - Err(_) => {} - } - Field::Static(value) - } -} - -impl From<&str> for Field { - fn from(value: &str) -> Self { - match Uuid::try_from(value) { - Ok(data) => return Field::Uuid(data), - Err(_) => {} - } - Field::Static(value.into()) - } -} - -impl From for Field { - fn from(value: Uuid) -> Self { - Field::Uuid(value) - } -} - -#[cfg(test)] -mod fields { - use super::*; - - #[test] - fn string_to_field() { - let entries = ["test1".to_string(), "test2".to_string()]; - for data in entries { - match data.clone().into() { - Field::Static(result) => assert_eq!(result, data), - _ => unreachable!("shouuld have been a static field"), - } - } +impl Reply { + pub fn get_session(&self) -> String { + "id".to_string() } - #[test] - fn str_to_field() { - let entries = ["test1", "test2"]; - for data in entries { - match data.into() { - Field::Static(result) => assert_eq!(result, data), - _ => unreachable!("shouuld have been a static field"), - } - } - } - - #[test] - fn uuid_to_field() { - let id = Uuid::new_v4(); - match id.into() { - Field::Uuid(result) => assert_eq!(result, id), - _ => unreachable!("should have been a uuid field"), - } - } - - #[test] - fn uuid_string_to_field() { - let id = Uuid::new_v4(); - let id_string = id.to_string(); - match id_string.into() { - Field::Uuid(result) => assert_eq!(result, id), - _ => unreachable!("should have been a uuid field"), - } - } - - #[test] - fn uuid_str_to_field() { - let id = Uuid::new_v4(); - let id_string = id.to_string(); - let id_str = id_string.as_str(); - match id_str.into() { - Field::Uuid(result) => assert_eq!(result, id), - _ => unreachable!("should have been a uuid field"), - } + pub fn get_content(&self) -> String { + "Something goes here.".to_string() } } #[derive(Clone)] -struct Request { - id: Option, - tx: Sender, -} - -impl Request { - fn new(id: Option) -> (Self, Receiver) - where - F: Into, - { - let result: Option; - match id { - Some(data) => { - result = Some(data.into()); - } - None => result = None, - } - let (tx, rx) = channel(); - (Self { id: result, tx: tx }, rx) - } - - fn get_session(&self) -> &Option { - return &self.id; - } -} - -#[cfg(test)] -mod create_request { - use super::*; - - pub fn empty_request() -> (Request, Receiver) { - let id: Option = None; - Request::new(id) - } -} - -#[cfg(test)] -mod requests { - use super::*; - - #[test] - fn create_request_no_id() { - let input: Option = None; - let (req, _) = Request::new(input); - assert!(req.id.is_none()); - } - - #[test] - fn create_request_with_uuid() { - let id = Uuid::new_v4(); - let (req, _) = Request::new(Some(id)); - match req.id { - Some(field) => match (field) { - Field::Uuid(data) => assert_eq!(data, id), - _ => unreachable!("Should have been a uuid"), - }, - None => unreachable!("Should producer data"), - } - } - - #[test] - fn return_session() { - let id = Uuid::new_v4(); - let (req, _) = Request::new(Some(id)); - match req.get_session() { - Some(result) => { - match result { - Field::Uuid(data) => assert_eq!(data, &id), - _ => unreachable!("should have returned a uuid field"), - }; - } - None => unreachable!("should have returned a uuid"), - } - } -} - -struct Record { - data: HashMap, -} - -impl Record { - fn new() -> Self { - Self { - data: HashMap::new(), - } - } - - fn add(&mut self, name: S, data: F) - where - S: Into, - F: Into, - { - self.data.insert(name.into(), data.into()); - } -} - -impl Deref for Record { - type Target = HashMap; - - fn deref(&self) -> &Self::Target { - &self.data - } -} - -#[cfg(test)] -mod records { - use super::*; - - #[test] - fn initialize() { - let rec = Record::new(); - assert!(rec.is_empty()); - } - - #[test] - fn bad_get_return() { - let rec = Record::new(); - match rec.get("empty") { - Some(_) => unreachable!("Should_have returned a None"), - None => {} - } - } - - #[test] - fn add_data() { - let name = "name1x"; - let data = "data1"; - let mut rec = Record::new(); - rec.add(name, data); - match rec.get(name) { - None => unreachable!("Should return data"), - Some(result) => match result { - Field::Static(txt) => assert_eq!(txt, data), - _ => unreachable!("Should have been static"), - }, - } - } - - #[test] - fn add_data_strings() { - let name = "field".to_string(); - let data = "info".to_string(); - let mut rec = Record::new(); - rec.add(name.clone(), data.clone()); - match rec.get(&name) { - None => unreachable!("Should return data"), - Some(result) => match result { - Field::Static(txt) => assert_eq!(txt, &data), - _ => unreachable!("should have been statis"), - }, - } - } -} - -#[derive(Debug)] -enum ResponseError { - ColumnNumberMisMatch, - MissionColumn(String), -} - -struct Response { - data: HashMap>, - counter: usize, -} - -impl Response { - fn new() -> Self { - Self { - data: HashMap::new(), - counter: 0, - } - } - - fn add(&mut self, rec: Record) -> Result<(), ResponseError> { - if self.data.is_empty() { - for (key, value) in rec.iter() { - let mut store = Vec::new(); - store.push(value.clone()); - self.data.insert(key.to_string(), store); - } - } else { - if rec.len() != self.data.len() { - return Err(ResponseError::ColumnNumberMisMatch); - } - for (key, value) in rec.iter() { - match self.data.get_mut(key) { - Some(data) => data.push(value.clone()), - None => return Err(ResponseError::MissionColumn(key.to_string())), - } - } - } - Ok(()) - } -} - -impl Iterator for Response { - type Item = Record; - - fn next(&mut self) -> Option { - if self.data.is_empty() { - return None; - } - let mut rec = Record::new(); - for (key, value) in self.data.iter() { - if self.counter >= value.len() { - return None; - } - rec.add(key, value[self.counter].clone()); - } - self.counter += 1; - Some(rec) - } -} - -#[cfg(test)] -mod responses { - use super::*; - - #[test] - fn create_response() { - let mut res = Response::new(); - assert!(res.data.is_empty()); - assert!(res.next().is_none()); - } - - #[test] - fn add_records() { - let mut res = Response::new(); - let columns = ["col1", "col2"]; - let count = 3; - let field_cnt = &count * columns.len(); - let mut fields = 0..field_cnt; - for _ in 0..count { - let mut rec = Record::new(); - for col in columns { - rec.add(col, fields.next().unwrap().to_string()); - } - res.add(rec); - } - fields = 0..field_cnt; - for _ in 0..count { - match res.next() { - Some(rec) => { - assert_eq!(rec.len(), columns.len()); - for col in columns { - match rec.get(col).unwrap() { - Field::Static(txt) => { - assert_eq!(txt.clone(), fields.next().unwrap().to_string()) - } - _ => unreachable!("should have been static"), - } - } - } - None => unreachable!("Should have returned data"), - } - } - assert!(res.next().is_none(), "exceeded the correct of records"); - } - - #[test] - fn number_of_columns_mismatch() { - let mut res = Response::new(); - let mut rec1 = Record::new(); - let mut rec2 = Record::new(); - rec1.add("one", "one"); - rec2.add("one", "one"); - rec2.add("two", "two"); - res.add(rec1).unwrap(); - match res.add(rec2) { - Ok(_) => unreachable!("Should not accept additional value"), - Err(err) => match err { - ResponseError::ColumnNumberMisMatch => {} - _ => unreachable!("should havee been a mismatch error"), - }, - } - } - - #[test] - fn missing_field_error() { - let mut res = Response::new(); - let mut rec1 = Record::new(); - let mut rec2 = Record::new(); - rec1.add("one", "one"); - rec2.add("one", "one"); - rec2.add("two", "two"); - res.add(rec2).unwrap(); - match res.add(rec1) { - Ok(_) => unreachable!("Should not accept additional value"), - Err(err) => match err { - ResponseError::ColumnNumberMisMatch => {} - _ => unreachable!("should havee been a mismatch error"), - }, - } - } - - #[test] - fn wrong_column_name() { - let mut res = Response::new(); - let mut rec1 = Record::new(); - let mut rec2 = Record::new(); - rec1.add("one", "one"); - rec2.add("two", "two"); - res.add(rec1).unwrap(); - match res.add(rec2) { - Ok(_) => unreachable!("Should not accept additional value"), - Err(err) => match err { - ResponseError::MissionColumn(txt) => assert_eq!(txt, "two"), - _ => unreachable!("should have been missing cloumn"), - }, - } - } -} - -/// Support functions for Messages. -pub trait Msg { - fn to_msgdata(&self) -> MsgData; -} - -#[cfg(test)] -mod test_message { - use super::*; - - pub enum Tester { - Test1, - Test2, - } - - impl Msg for Tester { - fn to_msgdata(&self) -> MsgData { - match self { - Tester::Test1 => MsgData::Test1, - Tester::Test2 => MsgData::Test2, - } - } - } -} - -/// Application client to MoreThanText -pub struct MoreThanText { - // tx: Sender, - session: Option, - tx: Sender, -} +pub struct MoreThanText; impl MoreThanText { - /// Create a MoreThanText database. - /// - /// Example: - /// - /// ``` - /// use morethantext::MoreThanText; - /// - /// MoreThanText::new(); - /// ``` pub fn new() -> Self { - /* Future code. - * tx = Queue::start(); - * tx.send(Session::start().into()).unwrap(); - * Self { tx: tx } - */ - let (tx, rx) = channel(); - let mut senders = Vec::new(); - senders.push(Client::start(tx.clone())); - senders.push(Session::start(tx.clone())); - Router::start(senders, rx); - Self { - session: None, - tx: tx, - } + Self {} } - /// Opens an existing or new session with the database. - /// - /// If the string is None, incorrect, or expired, - /// a new session will be created. - /// - /// Example: - /// - /// ``` - /// use morethantext::MoreThanText; - /// - /// let mut mtt = MoreThanText::new(); - /// mtt.open_session(None); - /// mtt.open_session(Some("7b1ff340-7dfa-4f29-b144-601384e54423".to_string())); - /// ``` - pub fn open_session(&mut self, id: Option) { - let (tx, rx) = channel(); - let req = ClientMsg::OpenSession(id, tx); - let msg = Message::new(&req); - self.tx.send(msg).unwrap(); - match rx.recv().unwrap().get_message() { - MsgData::Session(data) => match data { - SessionMsg::Opened(sess) => self.session = Some(sess.clone()), - _ => {} - }, - _ => {} - }; - } - - /// Get the session id - /// - /// Example: - /// - /// ``` - /// use morethantext::MoreThanText; - /// - /// let mut mtt = MoreThanText::new(); - /// let id = mtt.get_id(); - /// ``` - pub fn get_id(&self) -> String { - match &self.session { - Some(id) => id.to_string(), - None => "".to_string(), - } - } -} - -impl Clone for MoreThanText { - fn clone(&self) -> Self { - Self { - session: None, - tx: self.tx.clone(), - } - } -} - -#[cfg(test)] -mod mtt_client { - use super::*; - - #[test] - fn default_values() { - let mtt = MoreThanText::new(); - assert!(mtt.session.is_none()); - } - - #[test] - fn new_session() { - let mut mtt = MoreThanText::new(); - mtt.open_session(None); - assert!(mtt.session.is_some()); - } - - #[test] - fn session_ids_are_unique() { - let mut mtt = MoreThanText::new(); - mtt.open_session(None); - let id1 = mtt.get_id(); - mtt.open_session(None); - assert_ne!(mtt.get_id(), id1); - } - - #[test] - fn cloned_clients_have_no_session() { - let mut mtt = MoreThanText::new(); - mtt.open_session(None); - let result = mtt.clone(); - assert!(result.session.is_none()); + pub fn request(&self, _session: Option) -> Reply + where + F: Into, + { + Reply {} } } diff --git a/src/main.rs b/src/main.rs index 8a9ef75..faa32ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ -use axum::{extract::State, response::IntoResponse, routing::get, Router}; +use axum::{extract::State, handler::Handler, response::IntoResponse}; use axum_extra::extract::cookie::{Cookie, CookieJar}; use clap::Parser; -//use morethantext::{MoreThanText, Session}; use morethantext::MoreThanText; +use tokio::{spawn, sync::mpsc::channel}; const LOCALHOST: &str = "127.0.0.1"; const SESSION_KEY: &str = "sessionid"; @@ -21,28 +21,32 @@ struct Args { node: Vec, } -#[cfg(test)] -mod http_session { - #[tokio::test] - async fn my_test() { - assert!(true); - } -} - #[tokio::main] async fn main() { let args = Args::parse(); let addr = format!("{}:{}", args.address, args.port); let state = MoreThanText::new(); - let app = Router::new().route("/", get(handler)).with_state(state); + let app = mtt_conn.with_state(state); let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); axum::serve(listener, app.into_make_service()) .await .unwrap(); } -async fn handler(jar: CookieJar, mut state: State) -> impl IntoResponse { - let mut cookies = jar.clone(); +async fn mtt_conn(jar: CookieJar, state: State) -> impl IntoResponse { + let sid = match jar.get(SESSION_KEY) { + Some(cookie) => Some(cookie.value().to_string()), + None => None, + }; + let (tx, mut rx) = channel(5); + spawn(async move { + tx.send(state.request(sid)).await.unwrap(); + }); + let reply = rx.recv().await.unwrap(); + let cookie = Cookie::build((SESSION_KEY, reply.get_session())); + let cookies = jar.add(cookie); + + /* let sid = match jar.get(SESSION_KEY) { Some(cookie) => Some(cookie.value().to_string()), None => None, @@ -52,5 +56,50 @@ async fn handler(jar: CookieJar, mut state: State) -> impl IntoRes let cookie = Cookie::build((SESSION_KEY, state.get_id())); cookies = jar.add(cookie); } - (cookies, "Something goes here.") + */ + (cookies, reply.get_content()) +} + +#[cfg(test)] +mod servers { + use super::*; + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use tower::ServiceExt; + + #[tokio::test] + async fn get_home_page() { + let app = mtt_conn.with_state(MoreThanText::new()); + let response = app + .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap()) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + let sessid = format!("{:?}", response.headers().get("set-cookie").unwrap()); + assert!(sessid.contains(SESSION_KEY), "did not set session id"); + } + + /* + #[tokio::test] + async fn session_ids_are_unique() { + let app = mtt_conn.with_state(MoreThanText::new()); + let mut holder: Vec = Vec::new(); + for _ in 0..5 { + let response = app + .clone() + .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap()) + .await + .unwrap(); + let sessid = format!("{:?}", response.headers().get("set-cookie").unwrap()); + assert!( + !holder.contains(&sessid), + "found duplicate entry: {:?}", + holder + ); + holder.push(sessid); + } + } + */ } diff --git a/src/old-lib.rs b/src/old-lib.rs new file mode 100644 index 0000000..b369afa --- /dev/null +++ b/src/old-lib.rs @@ -0,0 +1,597 @@ +mod backend; +mod client; +mod data; +mod error; +mod frontend; +mod message; +mod queue; +mod router; +mod session; +mod session2; + +use client::{Client, ClientMsg}; +use message::{Message, MsgData}; +use router::Router; +use session::{Session, SessionData, SessionMsg}; +use std::{ + collections::HashMap, + ops::Deref, + sync::mpsc::{channel, Receiver, Sender}, + thread::spawn, +}; +use uuid::Uuid; + +/* + * This is code to finish once sessions have been ransfered, + * and the dead code is removed. + * +trait MessageService: Sized { + fn new(tx: Sender, rx: Receiver) -> Self; + + fn start(queue_tx: Sender) { + spawn(move || { + let (tx, rx) = channel(); + let service = Self::new(queue_tx.clone(), rx); + queue_tx.send(tx.clone().into()).unwrap(); + service.listen(); + }); + } + + fn listen(&self) { + loop { + self.rx.recv().unwrap(); + } + } +} +*/ + +#[derive(Clone)] +enum Field { + Static(String), + Uuid(Uuid), +} + +impl From for Field { + fn from(value: String) -> Self { + match Uuid::try_from(value.as_str()) { + Ok(data) => return Field::Uuid(data), + Err(_) => {} + } + Field::Static(value) + } +} + +impl From<&str> for Field { + fn from(value: &str) -> Self { + match Uuid::try_from(value) { + Ok(data) => return Field::Uuid(data), + Err(_) => {} + } + Field::Static(value.into()) + } +} + +impl From for Field { + fn from(value: Uuid) -> Self { + Field::Uuid(value) + } +} + +#[cfg(test)] +mod fields { + use super::*; + + #[test] + fn string_to_field() { + let entries = ["test1".to_string(), "test2".to_string()]; + for data in entries { + match data.clone().into() { + Field::Static(result) => assert_eq!(result, data), + _ => unreachable!("shouuld have been a static field"), + } + } + } + + #[test] + fn str_to_field() { + let entries = ["test1", "test2"]; + for data in entries { + match data.into() { + Field::Static(result) => assert_eq!(result, data), + _ => unreachable!("shouuld have been a static field"), + } + } + } + + #[test] + fn uuid_to_field() { + let id = Uuid::new_v4(); + match id.into() { + Field::Uuid(result) => assert_eq!(result, id), + _ => unreachable!("should have been a uuid field"), + } + } + + #[test] + fn uuid_string_to_field() { + let id = Uuid::new_v4(); + let id_string = id.to_string(); + match id_string.into() { + Field::Uuid(result) => assert_eq!(result, id), + _ => unreachable!("should have been a uuid field"), + } + } + + #[test] + fn uuid_str_to_field() { + let id = Uuid::new_v4(); + let id_string = id.to_string(); + let id_str = id_string.as_str(); + match id_str.into() { + Field::Uuid(result) => assert_eq!(result, id), + _ => unreachable!("should have been a uuid field"), + } + } +} + +#[derive(Clone)] +struct Request { + id: Option, + tx: Sender, +} + +impl Request { + fn new(id: Option) -> (Self, Receiver) + where + F: Into, + { + let result: Option; + match id { + Some(data) => { + result = Some(data.into()); + } + None => result = None, + } + let (tx, rx) = channel(); + (Self { id: result, tx: tx }, rx) + } + + fn get_session(&self) -> &Option { + return &self.id; + } +} + +#[cfg(test)] +mod create_request { + use super::*; + + pub fn empty_request() -> (Request, Receiver) { + let id: Option = None; + Request::new(id) + } +} + +#[cfg(test)] +mod requests { + use super::*; + + #[test] + fn create_request_no_id() { + let input: Option = None; + let (req, _) = Request::new(input); + assert!(req.id.is_none()); + } + + #[test] + fn create_request_with_uuid() { + let id = Uuid::new_v4(); + let (req, _) = Request::new(Some(id)); + match req.id { + Some(field) => match (field) { + Field::Uuid(data) => assert_eq!(data, id), + _ => unreachable!("Should have been a uuid"), + }, + None => unreachable!("Should producer data"), + } + } + + #[test] + fn return_session() { + let id = Uuid::new_v4(); + let (req, _) = Request::new(Some(id)); + match req.get_session() { + Some(result) => { + match result { + Field::Uuid(data) => assert_eq!(data, &id), + _ => unreachable!("should have returned a uuid field"), + }; + } + None => unreachable!("should have returned a uuid"), + } + } +} + +struct Record { + data: HashMap, +} + +impl Record { + fn new() -> Self { + Self { + data: HashMap::new(), + } + } + + fn add(&mut self, name: S, data: F) + where + S: Into, + F: Into, + { + self.data.insert(name.into(), data.into()); + } +} + +impl Deref for Record { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.data + } +} + +#[cfg(test)] +mod records { + use super::*; + + #[test] + fn initialize() { + let rec = Record::new(); + assert!(rec.is_empty()); + } + + #[test] + fn bad_get_return() { + let rec = Record::new(); + match rec.get("empty") { + Some(_) => unreachable!("Should_have returned a None"), + None => {} + } + } + + #[test] + fn add_data() { + let name = "name1x"; + let data = "data1"; + let mut rec = Record::new(); + rec.add(name, data); + match rec.get(name) { + None => unreachable!("Should return data"), + Some(result) => match result { + Field::Static(txt) => assert_eq!(txt, data), + _ => unreachable!("Should have been static"), + }, + } + } + + #[test] + fn add_data_strings() { + let name = "field".to_string(); + let data = "info".to_string(); + let mut rec = Record::new(); + rec.add(name.clone(), data.clone()); + match rec.get(&name) { + None => unreachable!("Should return data"), + Some(result) => match result { + Field::Static(txt) => assert_eq!(txt, &data), + _ => unreachable!("should have been statis"), + }, + } + } +} + +#[derive(Debug)] +enum ResponseError { + ColumnNumberMisMatch, + MissionColumn(String), +} + +struct Response { + data: HashMap>, + counter: usize, +} + +impl Response { + fn new() -> Self { + Self { + data: HashMap::new(), + counter: 0, + } + } + + fn add(&mut self, rec: Record) -> Result<(), ResponseError> { + if self.data.is_empty() { + for (key, value) in rec.iter() { + let mut store = Vec::new(); + store.push(value.clone()); + self.data.insert(key.to_string(), store); + } + } else { + if rec.len() != self.data.len() { + return Err(ResponseError::ColumnNumberMisMatch); + } + for (key, value) in rec.iter() { + match self.data.get_mut(key) { + Some(data) => data.push(value.clone()), + None => return Err(ResponseError::MissionColumn(key.to_string())), + } + } + } + Ok(()) + } +} + +impl Iterator for Response { + type Item = Record; + + fn next(&mut self) -> Option { + if self.data.is_empty() { + return None; + } + let mut rec = Record::new(); + for (key, value) in self.data.iter() { + if self.counter >= value.len() { + return None; + } + rec.add(key, value[self.counter].clone()); + } + self.counter += 1; + Some(rec) + } +} + +#[cfg(test)] +mod responses { + use super::*; + + #[test] + fn create_response() { + let mut res = Response::new(); + assert!(res.data.is_empty()); + assert!(res.next().is_none()); + } + + #[test] + fn add_records() { + let mut res = Response::new(); + let columns = ["col1", "col2"]; + let count = 3; + let field_cnt = &count * columns.len(); + let mut fields = 0..field_cnt; + for _ in 0..count { + let mut rec = Record::new(); + for col in columns { + rec.add(col, fields.next().unwrap().to_string()); + } + res.add(rec); + } + fields = 0..field_cnt; + for _ in 0..count { + match res.next() { + Some(rec) => { + assert_eq!(rec.len(), columns.len()); + for col in columns { + match rec.get(col).unwrap() { + Field::Static(txt) => { + assert_eq!(txt.clone(), fields.next().unwrap().to_string()) + } + _ => unreachable!("should have been static"), + } + } + } + None => unreachable!("Should have returned data"), + } + } + assert!(res.next().is_none(), "exceeded the correct of records"); + } + + #[test] + fn number_of_columns_mismatch() { + let mut res = Response::new(); + let mut rec1 = Record::new(); + let mut rec2 = Record::new(); + rec1.add("one", "one"); + rec2.add("one", "one"); + rec2.add("two", "two"); + res.add(rec1).unwrap(); + match res.add(rec2) { + Ok(_) => unreachable!("Should not accept additional value"), + Err(err) => match err { + ResponseError::ColumnNumberMisMatch => {} + _ => unreachable!("should havee been a mismatch error"), + }, + } + } + + #[test] + fn missing_field_error() { + let mut res = Response::new(); + let mut rec1 = Record::new(); + let mut rec2 = Record::new(); + rec1.add("one", "one"); + rec2.add("one", "one"); + rec2.add("two", "two"); + res.add(rec2).unwrap(); + match res.add(rec1) { + Ok(_) => unreachable!("Should not accept additional value"), + Err(err) => match err { + ResponseError::ColumnNumberMisMatch => {} + _ => unreachable!("should havee been a mismatch error"), + }, + } + } + + #[test] + fn wrong_column_name() { + let mut res = Response::new(); + let mut rec1 = Record::new(); + let mut rec2 = Record::new(); + rec1.add("one", "one"); + rec2.add("two", "two"); + res.add(rec1).unwrap(); + match res.add(rec2) { + Ok(_) => unreachable!("Should not accept additional value"), + Err(err) => match err { + ResponseError::MissionColumn(txt) => assert_eq!(txt, "two"), + _ => unreachable!("should have been missing cloumn"), + }, + } + } +} + +/// Support functions for Messages. +pub trait Msg { + fn to_msgdata(&self) -> MsgData; +} + +#[cfg(test)] +mod test_message { + use super::*; + + pub enum Tester { + Test1, + Test2, + } + + impl Msg for Tester { + fn to_msgdata(&self) -> MsgData { + match self { + Tester::Test1 => MsgData::Test1, + Tester::Test2 => MsgData::Test2, + } + } + } +} + +/// Application client to MoreThanText +pub struct MoreThanText { + // tx: Sender, + session: Option, + tx: Sender, +} + +impl MoreThanText { + /// Create a MoreThanText database. + /// + /// Example: + /// + /// ``` + /// use morethantext::MoreThanText; + /// + /// MoreThanText::new(); + /// ``` + pub fn new() -> Self { + /* Future code. + * tx = Queue::start(); + * tx.send(Session::start().into()).unwrap(); + * Self { tx: tx } + */ + let (tx, rx) = channel(); + let mut senders = Vec::new(); + senders.push(Client::start(tx.clone())); + senders.push(Session::start(tx.clone())); + Router::start(senders, rx); + Self { + session: None, + tx: tx, + } + } + + /// Opens an existing or new session with the database. + /// + /// If the string is None, incorrect, or expired, + /// a new session will be created. + /// + /// Example: + /// + /// ``` + /// use morethantext::MoreThanText; + /// + /// let mut mtt = MoreThanText::new(); + /// mtt.open_session(None); + /// mtt.open_session(Some("7b1ff340-7dfa-4f29-b144-601384e54423".to_string())); + /// ``` + pub fn open_session(&mut self, id: Option) { + let (tx, rx) = channel(); + let req = ClientMsg::OpenSession(id, tx); + let msg = Message::new(&req); + self.tx.send(msg).unwrap(); + match rx.recv().unwrap().get_message() { + MsgData::Session(data) => match data { + SessionMsg::Opened(sess) => self.session = Some(sess.clone()), + _ => {} + }, + _ => {} + }; + } + + /// Get the session id + /// + /// Example: + /// + /// ``` + /// use morethantext::MoreThanText; + /// + /// let mut mtt = MoreThanText::new(); + /// let id = mtt.get_id(); + /// ``` + pub fn get_id(&self) -> String { + match &self.session { + Some(id) => id.to_string(), + None => "".to_string(), + } + } +} + +impl Clone for MoreThanText { + fn clone(&self) -> Self { + Self { + session: None, + tx: self.tx.clone(), + } + } +} + +#[cfg(test)] +mod mtt_client { + use super::*; + + #[test] + fn default_values() { + let mtt = MoreThanText::new(); + assert!(mtt.session.is_none()); + } + + #[test] + fn new_session() { + let mut mtt = MoreThanText::new(); + mtt.open_session(None); + assert!(mtt.session.is_some()); + } + + #[test] + fn session_ids_are_unique() { + let mut mtt = MoreThanText::new(); + mtt.open_session(None); + let id1 = mtt.get_id(); + mtt.open_session(None); + assert_ne!(mtt.get_id(), id1); + } + + #[test] + fn cloned_clients_have_no_session() { + let mut mtt = MoreThanText::new(); + mtt.open_session(None); + let result = mtt.clone(); + assert!(result.session.is_none()); + } +} diff --git a/src/old-queue.rs b/src/old-queue.rs new file mode 100644 index 0000000..d3cd020 --- /dev/null +++ b/src/old-queue.rs @@ -0,0 +1,156 @@ +use crate::{backend::BackEndMessage, session2::SessionMessage, Request}; +use std::{ + sync::mpsc::{channel, Receiver, Sender}, + thread::spawn, +}; + +#[derive(Clone)] +pub enum Message { + BEMsg(BackEndMessage), + Register(Sender), + Req(Request), + SessMsg(SessionMessage), +} + +impl From for Message { + fn from(value: Request) -> Self { + Message::Req(value) + } +} + +impl From> for Message { + fn from(value: Sender) -> Self { + Message::Register(value) + } +} + +impl From for Message { + fn from(value: SessionMessage) -> Self { + Message::SessMsg(value) + } +} + +impl From for Message { + fn from(value: BackEndMessage) -> Self { + Message::BEMsg(value) + } +} + +#[cfg(test)] +mod messages { + use super::*; + use crate::{create_request::empty_request, Field}; + + #[test] + fn from_request() { + let (req, _) = empty_request(); + match req.into() { + Message::Req(result) => assert!(result.get_session().is_none()), + _ => unreachable!("should have been s request"), + } + } + + #[test] + fn from_sender() { + let (tx, rx) = channel(); + match tx.into() { + Message::Register(result) => { + let (req, _) = empty_request(); + result.send(req.into()).unwrap(); + match rx.recv().unwrap() { + Message::Req(_) => {} + _ => unreachable!("should have been a request"), + } + } + _ => unreachable!("should have been a register"), + } + } + + #[test] + fn from_sessionmessage() { + let id: Option = None; + let sess_msg: SessionMessage = id.into(); + match sess_msg.into() { + Message::SessMsg(result) => match result { + SessionMessage::Validate(data) => assert!(data.is_none()), + _ => unreachable!("should have been a validate"), + }, + _ => unreachable!("should have been a session message"), + } + } + + #[test] + fn from_backend_message() { + let data = BackEndMessage::Get; + match data.into() { + Message::BEMsg(result) => match result { + BackEndMessage::Get => {} + _ => unreachable!("should have been a validate"), + }, + _ => unreachable!("should have been a session message"), + } + } +} + +struct Queue { + channels: Vec>, + rx: Receiver, +} + +impl Queue { + fn new(rx: Receiver) -> Self { + Self { + channels: Vec::new(), + rx: rx, + } + } + + fn listen(&mut self) { + loop { + let msg = self.rx.recv().unwrap(); + match msg { + Message::Register(tx) => self.channels.push(tx), + _ => { + for tx in self.channels.iter() { + tx.send(msg.clone()).unwrap(); + } + } + } + } + } + + fn start() -> Sender { + let (tx, rx) = channel(); + spawn(move || { + let mut queue = Queue::new(rx); + queue.listen(); + }); + tx + } +} + +#[cfg(test)] +mod queues { + use super::*; + use crate::create_request::empty_request; + + #[test] + fn create_queue() { + let mut channels = Vec::new(); + for _ in 0..5 { + channels.push(channel()); + } + let mut queue_tx = Queue::start(); + for (tx, _) in channels.iter() { + queue_tx.send(tx.clone().into()).unwrap(); + } + let (req, _) = empty_request(); + queue_tx.send(req.into()).unwrap(); + for (_, rx) in channels.iter() { + match rx.recv().unwrap() { + Message::Req(_) => {} + _ => unreachable!("should have been a request"), + } + } + } +} diff --git a/src/queue.rs b/src/queue.rs index 6c69303..c6ab1a4 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,20 +1,224 @@ -use crate::{session2::SessionMessage, Request}; +use crate::{client::Reply, ClientMessage, Request}; use std::{ sync::mpsc::{channel, Receiver, Sender}, thread::spawn, }; +#[derive(Clone)] +pub enum MessageData { + ClientMsg(ClientMessage), + Register(Sender), +} + +impl From for MessageData { + fn from(value: ClientMessage) -> Self { + MessageData::ClientMsg(value) + } +} + +impl From for MessageData { + fn from(value: Request) -> Self { + let data: ClientMessage = value.into(); + MessageData::ClientMsg(data) + } +} + +impl From> for MessageData { + fn from(value: Sender) -> Self { + MessageData::Register(value) + } +} + +#[cfg(test)] +mod messagedatas { + use crate::create_requests::root_doc_no_sess; + use super::*; + + #[test] + fn from_client_message() { + let (req, _) = root_doc_no_sess(); + let data: ClientMessage = req.into(); + match data.into() { + MessageData::ClientMsg(_) => {}, + _ => unreachable!("should have been a client messsage"), + } + } + + #[test] + fn from_request() { + let (req, _) = root_doc_no_sess(); + match req.into() { + MessageData::ClientMsg(_) => {}, + _ => unreachable!("should be a client message"), + } + } + + #[test] + fn from_sender() { + let (tx, rx) = channel(); + match tx.into() { + MessageData::Register(result) => { + let (req, _) = root_doc_no_sess(); + result.send(Message::new(req.into())).unwrap(); + match rx.recv().unwrap().data { + MessageData::ClientMsg(_) => {} + _ => unreachable!("should have been a request"), + } + } + _ => unreachable!("should have been a register"), + } + } +} + +#[derive(Clone)] +pub struct Message { + data: MessageData, +} + +impl Message { + pub fn new(data: MessageData) -> Self { + Self { + data: data, + } + } + + pub fn get_data(&self) -> &MessageData { + &self.data + } +} + +#[cfg(test)] +mod messages { + use crate::create_requests::root_doc_no_sess; + use super::*; + + #[test] + fn create_msg() { + let (req, _) = root_doc_no_sess(); + let msg = Message::new(req.into()); + match msg.data { + MessageData::ClientMsg(_) => {}, + _ => unreachable!("should have been a client message"), + } + } +} + +pub struct Queue { + channels: Vec>, + rx: Receiver, +} + +impl Queue { + fn new(rx: Receiver) -> Self { + Self { + channels: Vec::new(), + rx: rx, + } + } + + pub fn start() -> Sender { + let (tx, rx) = channel(); + spawn(move || { + let mut queue = Queue::new(rx); + queue.listen(); + }); + tx + } + + fn listen(&mut self) { + loop { + let msg = self.rx.recv().unwrap(); + match msg.get_data() { + MessageData::Register(sender) => self.channels.push(sender.clone()), + _ => { + for tx in self.channels.iter() { + tx.send(msg.clone()).unwrap(); + } + }, + } + } + } +} + +#[cfg(test)] +mod queues { + use crate::{ + create_requests::root_doc_no_sess, + Request, + }; + use std::time::Duration; + use super::*; + + #[test] + fn create_queue() { + let mut channels = Vec::new(); + for _ in 0..5 { + channels.push(channel()); + } + let queue_tx = Queue::start(); + for (tx, _) in channels.iter() { + queue_tx.send(Message::new(tx.clone().into())).unwrap(); + } + let (req, _) = root_doc_no_sess(); + queue_tx.send(Message::new(req.into())).unwrap(); + for (_, rx) in channels.iter() { + match rx.recv_timeout(Duration::from_millis(500)).unwrap().get_data() { + MessageData::ClientMsg(_) => {} + _ => unreachable!("should have been a request"), + } + } + } + + /* + #[test] + fn get_root_doc() { + let (client_tx, client_rx) = channel(); + let sess = None; + let req = Request { + tx: client_tx, + session: sess, + }; + let tx = Queue::start(); + tx.send(Message::new(req.into())).unwrap(); + client_rx.recv().unwrap(); + } + */ +} + + +/* +use crate::Request; +use std::{ + sync::mpsc::{channel, Receiver, Sender}, + thread::spawn, +}; + +pub trait QueueClient: Sized { + fn new(tx: Sender, rx: Receiver) -> Self; + + fn start(queue_tx: Sender) { + spawn(move || { + let (tx, rx) = channel(); + let service = Self::new(queue_tx.clone(), rx); + queue_tx.send(tx.into()).unwrap(); + service.listen(); + }); + } + + fn get_receiver(&self) -> &Receiver; + + fn listen(&self) { + let rx = self.get_receiver(); + loop { + rx.recv().unwrap(); + } + } +} + #[derive(Clone)] pub enum Message { Register(Sender), Req(Request), - SessMsg(SessionMessage), -} - -impl From for Message { - fn from(value: Request) -> Self { - Message::Req(value) - } } impl From> for Message { @@ -23,22 +227,22 @@ impl From> for Message { } } -impl From for Message { - fn from(value: SessionMessage) -> Self { - Message::SessMsg(value) +impl From for Message { + fn from(value: Request) -> Self { + Message::Req(value) } } #[cfg(test)] mod messages { use super::*; - use crate::{create_request::empty_request, Field}; + use crate::request_test_data::request_root_document; #[test] fn from_request() { - let (req, _) = empty_request(); + let (req, _) = request_root_document(); match req.into() { - Message::Req(result) => assert!(result.get_session().is_none()), + Message::Req(_) => {}, _ => unreachable!("should have been s request"), } } @@ -48,7 +252,7 @@ mod messages { let (tx, rx) = channel(); match tx.into() { Message::Register(result) => { - let (req, _) = empty_request(); + let (req, _) = request_root_document(); result.send(req.into()).unwrap(); match rx.recv().unwrap() { Message::Req(_) => {} @@ -58,22 +262,9 @@ mod messages { _ => unreachable!("should have been a register"), } } - - #[test] - fn from_sessionmessage() { - let id: Option = None; - let sess_msg: SessionMessage = id.into(); - match sess_msg.into() { - Message::SessMsg(result) => match result { - SessionMessage::Validate(data) => assert!(data.is_none()), - _ => unreachable!("should have been a validate"), - }, - _ => unreachable!("should have been a session message"), - } - } } -struct Queue { +pub struct Queue { channels: Vec>, rx: Receiver, } @@ -100,7 +291,7 @@ impl Queue { } } - fn start() -> Sender { + pub fn start() -> Sender { let (tx, rx) = channel(); spawn(move || { let mut queue = Queue::new(rx); @@ -113,7 +304,7 @@ impl Queue { #[cfg(test)] mod queues { use super::*; - use crate::create_request::empty_request; + use crate::request_test_data::request_root_document; #[test] fn create_queue() { @@ -121,11 +312,11 @@ mod queues { for _ in 0..5 { channels.push(channel()); } - let mut queue_tx = Queue::start(); + let queue_tx = Queue::start(); for (tx, _) in channels.iter() { queue_tx.send(tx.clone().into()).unwrap(); } - let (req, _) = empty_request(); + let (req, _) = request_root_document(); queue_tx.send(req.into()).unwrap(); for (_, rx) in channels.iter() { match rx.recv().unwrap() { @@ -135,3 +326,4 @@ mod queues { } } } +*/