2025-07-17 09:12:23 -04:00
|
|
|
use crate::field::Field;
|
|
|
|
|
use std::{
|
|
|
|
|
collections::HashMap,
|
|
|
|
|
sync::{
|
|
|
|
|
mpsc::{channel, Receiver, Sender},
|
|
|
|
|
Arc, RwLock,
|
|
|
|
|
},
|
|
|
|
|
};
|
2025-07-04 10:25:37 -04:00
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
enum MTTError {
|
|
|
|
|
DocumentAlreadyExists(String),
|
|
|
|
|
DocumentNotFound(String),
|
2025-07-17 09:12:23 -04:00
|
|
|
RouteNoListeners,
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
|
2025-07-17 09:12:23 -04:00
|
|
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
2025-07-04 10:25:37 -04:00
|
|
|
enum Action {
|
2025-07-17 09:12:23 -04:00
|
|
|
NewDocumentType,
|
2025-07-04 10:25:37 -04:00
|
|
|
Query,
|
|
|
|
|
Reply,
|
|
|
|
|
Update,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
2025-07-17 09:12:23 -04:00
|
|
|
enum NameID {
|
2025-07-04 10:25:37 -04:00
|
|
|
ID(Uuid),
|
|
|
|
|
Name(String),
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-17 09:12:23 -04:00
|
|
|
impl From<&str> for NameID {
|
2025-07-04 10:25:37 -04:00
|
|
|
fn from(value: &str) -> Self {
|
|
|
|
|
Self::Name(value.to_string())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-17 09:12:23 -04:00
|
|
|
impl From<String> for NameID {
|
2025-07-04 10:25:37 -04:00
|
|
|
fn from(value: String) -> Self {
|
|
|
|
|
Self::Name(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-17 09:12:23 -04:00
|
|
|
impl From<Uuid> for NameID {
|
2025-07-04 10:25:37 -04:00
|
|
|
fn from(value: Uuid) -> Self {
|
|
|
|
|
Self::ID(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
struct Message {
|
|
|
|
|
msg_id: Uuid,
|
2025-07-17 09:12:23 -04:00
|
|
|
document_id: NameID,
|
2025-07-04 10:25:37 -04:00
|
|
|
action: Action,
|
|
|
|
|
//instructions: ?,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Message {
|
2025-07-17 09:12:23 -04:00
|
|
|
fn new<D>(doc_id: D, action: Action) -> Self
|
|
|
|
|
where
|
|
|
|
|
D: Into<NameID>,
|
|
|
|
|
{
|
2025-07-04 10:25:37 -04:00
|
|
|
Self {
|
|
|
|
|
msg_id: Uuid::new_v4(),
|
|
|
|
|
document_id: doc_id.into(),
|
2025-07-17 09:12:23 -04:00
|
|
|
action: action,
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_message_id(&self) -> &Uuid {
|
|
|
|
|
&self.msg_id
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-17 09:12:23 -04:00
|
|
|
fn get_document_id(&self) -> &NameID {
|
2025-07-04 10:25:37 -04:00
|
|
|
&self.document_id
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-17 09:12:23 -04:00
|
|
|
fn get_action(&self) -> &Action {
|
|
|
|
|
&self.action
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod messages {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_the_document_be_a_stringi_reference() {
|
|
|
|
|
let dts = ["one", "two"];
|
|
|
|
|
for document in dts.into_iter() {
|
2025-07-17 09:12:23 -04:00
|
|
|
let msg = Message::new(document, Action::NewDocumentType);
|
2025-07-04 10:25:37 -04:00
|
|
|
match msg.get_document_id() {
|
2025-07-17 09:12:23 -04:00
|
|
|
NameID::ID(_) => unreachable!("should have been a string id"),
|
|
|
|
|
NameID::Name(data) => assert_eq!(data, document),
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
2025-07-17 09:12:23 -04:00
|
|
|
assert_eq!(msg.get_action(), &Action::NewDocumentType);
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_the_document_be_a_string() {
|
|
|
|
|
let dts = ["one".to_string(), "two".to_string()];
|
|
|
|
|
for document in dts.into_iter() {
|
|
|
|
|
let msg = Message::new(document.clone(), Action::Update);
|
|
|
|
|
match msg.get_document_id() {
|
2025-07-17 09:12:23 -04:00
|
|
|
NameID::ID(_) => unreachable!("should have been a string id"),
|
|
|
|
|
NameID::Name(data) => assert_eq!(data, &document),
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
2025-07-17 09:12:23 -04:00
|
|
|
assert_eq!(msg.get_action(), &Action::Update);
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_the_document_be_an_id() {
|
|
|
|
|
let document = Uuid::new_v4();
|
|
|
|
|
let msg = Message::new(document.clone(), Action::Query);
|
|
|
|
|
match msg.get_document_id() {
|
2025-07-17 09:12:23 -04:00
|
|
|
NameID::ID(data) => assert_eq!(data, &document),
|
|
|
|
|
NameID::Name(_) => unreachable!("should have been an id"),
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
assert_eq!(msg.action, Action::Query);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn is_the_message_id_random() {
|
|
|
|
|
let mut ids: Vec<Uuid> = Vec::new();
|
|
|
|
|
for _ in 0..5 {
|
2025-07-17 09:12:23 -04:00
|
|
|
let msg = Message::new("tester", Action::NewDocumentType);
|
2025-07-04 10:25:37 -04:00
|
|
|
let id = msg.get_message_id().clone();
|
|
|
|
|
assert!(!ids.contains(&id), "{:?} containts {}", ids, id);
|
|
|
|
|
ids.push(id);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-17 09:12:23 -04:00
|
|
|
}
|
2025-07-04 10:25:37 -04:00
|
|
|
|
2025-07-17 09:12:23 -04:00
|
|
|
#[derive(Eq, Hash, PartialEq)]
|
|
|
|
|
struct Route {
|
|
|
|
|
action: Action,
|
|
|
|
|
doc_type: Option<Uuid>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Route {
|
|
|
|
|
fn new(doc_type: Option<Uuid>, action: Action) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
action: action,
|
|
|
|
|
doc_type: doc_type,
|
|
|
|
|
}
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct QueueData {
|
|
|
|
|
senders: HashMap<Uuid, Sender<Message>>,
|
|
|
|
|
names: HashMap<String, Uuid>,
|
2025-07-17 09:12:23 -04:00
|
|
|
routes: HashMap<Route, Uuid>,
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl QueueData {
|
|
|
|
|
fn new() -> Self {
|
2025-07-17 09:12:23 -04:00
|
|
|
Self {
|
2025-07-04 10:25:37 -04:00
|
|
|
senders: HashMap::new(),
|
|
|
|
|
names: HashMap::new(),
|
2025-07-17 09:12:23 -04:00
|
|
|
routes: HashMap::new(),
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn register(&mut self, name: String, tx: Sender<Message>) -> Result<Uuid, MTTError> {
|
|
|
|
|
match self.names.get(&name) {
|
|
|
|
|
Some(_) => return Err(MTTError::DocumentAlreadyExists(name)),
|
2025-07-17 09:12:23 -04:00
|
|
|
None => {}
|
|
|
|
|
}
|
|
|
|
|
let mut id = Uuid::new_v4();
|
|
|
|
|
while self.senders.contains_key(&id) {
|
|
|
|
|
id = Uuid::new_v4();
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
self.senders.insert(id.clone(), tx);
|
|
|
|
|
self.names.insert(name, id.clone());
|
|
|
|
|
Ok(id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn send(&self, msg: Message) -> Result<(), MTTError> {
|
2025-07-17 09:12:23 -04:00
|
|
|
let doc_id = match msg.get_document_id() {
|
|
|
|
|
NameID::Name(name) => match self.names.get(name) {
|
|
|
|
|
Some(id) => Some(id.clone()),
|
|
|
|
|
None => return Err(MTTError::DocumentNotFound(name.clone())),
|
2025-07-04 10:25:37 -04:00
|
|
|
},
|
2025-07-17 09:12:23 -04:00
|
|
|
NameID::ID(id) => Some(id.clone()),
|
2025-07-04 10:25:37 -04:00
|
|
|
};
|
2025-07-17 09:12:23 -04:00
|
|
|
let route = Route::new(doc_id, msg.get_action().clone());
|
|
|
|
|
let sender_id = match self.routes.get(&route) {
|
|
|
|
|
Some(sender_id) => sender_id,
|
|
|
|
|
None => return Ok(()),
|
|
|
|
|
};
|
|
|
|
|
let tx = self.senders.get(sender_id).unwrap();
|
2025-07-04 10:25:37 -04:00
|
|
|
tx.send(msg).unwrap();
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2025-07-17 09:12:23 -04:00
|
|
|
|
|
|
|
|
fn add_route(
|
|
|
|
|
&mut self,
|
|
|
|
|
sender_id: &Uuid,
|
|
|
|
|
doc_type: Option<String>,
|
|
|
|
|
action: Action,
|
|
|
|
|
) -> Result<(), MTTError> {
|
|
|
|
|
let doc_id = match doc_type {
|
|
|
|
|
Some(name) => Some(self.names.get(&name).unwrap().clone()),
|
|
|
|
|
None => None,
|
|
|
|
|
};
|
|
|
|
|
let route = Route::new(doc_id, action);
|
|
|
|
|
self.routes.insert(route, sender_id.clone());
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod queuedatas {
|
|
|
|
|
use super::*;
|
2025-07-17 09:12:23 -04:00
|
|
|
use std::{sync::mpsc::RecvTimeoutError, time::Duration};
|
2025-07-04 10:25:37 -04:00
|
|
|
|
|
|
|
|
static TIMEOUT: Duration = Duration::from_millis(500);
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_a_new_document_type_be_rgistered() {
|
|
|
|
|
let name = Uuid::new_v4().to_string();
|
2025-07-17 09:12:23 -04:00
|
|
|
let action = Action::Query;
|
2025-07-04 10:25:37 -04:00
|
|
|
let (tx, rx) = channel();
|
|
|
|
|
let mut queuedata = QueueData::new();
|
|
|
|
|
let id = queuedata.register(name.clone(), tx).unwrap();
|
2025-07-17 09:12:23 -04:00
|
|
|
queuedata.add_route(&id, Some(name.clone()), action);
|
2025-07-04 10:25:37 -04:00
|
|
|
let msg = Message::new(name.clone(), Action::Query);
|
|
|
|
|
queuedata.send(msg.clone()).unwrap();
|
|
|
|
|
let result = rx.recv_timeout(TIMEOUT).unwrap();
|
|
|
|
|
assert_eq!(result.get_message_id(), msg.get_message_id());
|
|
|
|
|
let msg = Message::new(id.clone(), Action::Query);
|
|
|
|
|
queuedata.send(msg.clone()).unwrap();
|
|
|
|
|
let result = rx.recv_timeout(TIMEOUT).unwrap();
|
|
|
|
|
assert_eq!(result.get_message_id(), msg.get_message_id());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn does_a_bad_document_name_fail() {
|
|
|
|
|
let docname = Uuid::new_v4().to_string();
|
|
|
|
|
let queuedata = QueueData::new();
|
|
|
|
|
let msg = Message::new(docname.clone(), Action::Query);
|
|
|
|
|
match queuedata.send(msg) {
|
|
|
|
|
Ok(_) => unreachable!("should have been an error"),
|
|
|
|
|
Err(data) => match data {
|
|
|
|
|
MTTError::DocumentNotFound(doc) => assert_eq!(doc, docname),
|
|
|
|
|
_ => unreachable!("should have been a not found error"),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn should_error_on_duplicate_name_registration() {
|
|
|
|
|
let name = Uuid::new_v4().to_string();
|
|
|
|
|
let (tx1, _) = channel();
|
|
|
|
|
let (tx2, _) = channel();
|
|
|
|
|
let mut queuedata = QueueData::new();
|
|
|
|
|
queuedata.register(name.clone(), tx1).unwrap();
|
|
|
|
|
match queuedata.register(name.clone(), tx2) {
|
|
|
|
|
Ok(_) => unreachable!("should have been an weeoe"),
|
|
|
|
|
Err(data) => match data {
|
|
|
|
|
MTTError::DocumentAlreadyExists(output) => assert_eq!(output, name),
|
|
|
|
|
_ => unreachable!("should have been an already exists errorr"),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-17 09:12:23 -04:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn is_send_okay_if_no_one_is_listening() {
|
|
|
|
|
let mut queuedata = QueueData::new();
|
|
|
|
|
let name = "something";
|
|
|
|
|
let (tx, _) = channel();
|
|
|
|
|
queuedata.register(name.to_string(), tx).unwrap();
|
|
|
|
|
let msg = Message::new("something", Action::NewDocumentType);
|
|
|
|
|
match queuedata.send(msg) {
|
|
|
|
|
Ok(_) => {}
|
|
|
|
|
Err(err) => unreachable!("got {:?}: should not error", err),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_certain_messages_be_ignored() {
|
|
|
|
|
let mut queuedata = QueueData::new();
|
|
|
|
|
let doctype = "test";
|
|
|
|
|
let (tx, rx) = channel();
|
|
|
|
|
let id = queuedata.register(doctype.to_string(), tx).unwrap();
|
|
|
|
|
queuedata.add_route(&id, Some(doctype.to_string()), Action::Query);
|
|
|
|
|
let msg = Message::new(doctype, Action::Query);
|
|
|
|
|
queuedata.send(msg.clone()).unwrap();
|
|
|
|
|
let result = rx.recv_timeout(TIMEOUT).unwrap();
|
|
|
|
|
assert_eq!(result.get_message_id(), msg.get_message_id());
|
|
|
|
|
let msg = Message::new(doctype, Action::Reply);
|
|
|
|
|
match rx.recv_timeout(TIMEOUT) {
|
|
|
|
|
Ok(_) => unreachable!("should timeout"),
|
|
|
|
|
Err(err) => match err {
|
|
|
|
|
RecvTimeoutError::Timeout => {}
|
|
|
|
|
_ => unreachable!("should timeout"),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_messages_be_directed() {
|
|
|
|
|
let mut queuedata = QueueData::new();
|
|
|
|
|
let (tx1, rx1) = channel();
|
|
|
|
|
let (tx2, rx2) = channel();
|
|
|
|
|
let id1 = queuedata.register("task".to_string(), tx1);
|
|
|
|
|
let id2 = queuedata.register("work".to_string(), tx2);
|
|
|
|
|
let msg = Message::new("task".to_string(), Action::Query);
|
|
|
|
|
queuedata.send(msg.clone()).unwrap();
|
|
|
|
|
let result = rx1.recv_timeout(TIMEOUT).unwrap();
|
|
|
|
|
assert_eq!(result.get_message_id(), msg.get_message_id());
|
|
|
|
|
match rx2.recv_timeout(TIMEOUT) {
|
|
|
|
|
Ok(_) => unreachable!("should timeout"),
|
|
|
|
|
Err(err) => match err {
|
|
|
|
|
RecvTimeoutError::Timeout => {},
|
|
|
|
|
_ => unreachable!("should timeout"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*/
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
struct Queue {
|
|
|
|
|
queue_data: Arc<RwLock<QueueData>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Queue {
|
|
|
|
|
fn new() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
queue_data: Arc::new(RwLock::new(QueueData::new())),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod queues {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn create_a_queue() {
|
|
|
|
|
Queue::new();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Document;
|
|
|
|
|
|
|
|
|
|
impl Document {
|
|
|
|
|
fn new() -> Self {
|
|
|
|
|
Self {}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-17 09:12:23 -04:00
|
|
|
fn start(queue: Queue) {}
|
2025-07-04 10:25:37 -04:00
|
|
|
|
2025-07-17 09:12:23 -04:00
|
|
|
fn listen(&self) {}
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod documents {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn create_document_creation() {
|
|
|
|
|
let queue = Queue::new();
|
2025-07-17 09:12:23 -04:00
|
|
|
Document::start(queue.clone());
|
2025-07-04 10:25:37 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-17 09:12:23 -04:00
|
|
|
// Create a double hash map. posswible names that leads to an id that is int eh ids
|
|
|
|
|
// \and the second is the id and the sender to be used.and a third for who wants to
|
|
|
|
|
// listen to what.
|
2025-07-04 10:25:37 -04:00
|
|
|
//
|
|
|
|
|
// The queue has a read write lock on the abbove strucutee. A clone of this is given to
|
2025-07-17 09:12:23 -04:00
|
|
|
// every process.
|