diff --git a/src/message.rs b/src/message.rs index 25d9563..5a6c180 100644 --- a/src/message.rs +++ b/src/message.rs @@ -27,6 +27,10 @@ enum MTTError { DocumentFieldWrongDataType(FieldType, FieldType), DocumentNotFound(String), FieldDuplicate(String, Field), + NameDuplicate(Name), + NameInvalidID(Uuid), + NameMissingTranslation(Language), + NameNotFound(Name), QueryCannotChangeData, } @@ -77,7 +81,7 @@ impl NameID { fn is_none(&self) -> bool { match self { Self::None => true, - _ => false + _ => false, } } } @@ -397,7 +401,7 @@ mod messages { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, Hash)] enum Include { All, Some(T), @@ -458,19 +462,30 @@ impl From for RouteID { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] struct Name { name: String, lang: Language, } impl Name { + fn get_language(&self) -> &Language { + &self.lang + } + fn english(name: String) -> Self { Self { name: name, lang: Language::from_639_1("en").unwrap(), } } + + fn japanese(name: String) -> Self { + Self { + name: name, + lang: Language::from_639_1("ja").unwrap(), + } + } } impl ToString for Name { @@ -479,8 +494,226 @@ impl ToString for Name { } } +#[derive(Debug)] +struct Names { + names: HashMap, + ids: HashMap>, +} + +impl Names { + fn new() -> Self { + Self { + names: HashMap::new(), + ids: HashMap::new(), + } + } + + fn add_name(&mut self, name: Name) -> Result { + if self.names.contains_key(&name) { + return Err(MTTError::NameDuplicate(name)); + } + let mut id = Uuid::new_v4(); + while self.ids.contains_key(&id) { + id = Uuid::new_v4(); + } + self.names.insert(name.clone(), id.clone()); + let mut holder: HashMap = HashMap::new(); + holder.insert(name.get_language().clone(), name); + self.ids.insert(id.clone(), holder); + Ok(id) + } + + fn add_translation(&mut self, name: Name, translation: Name) -> Result { + let id = match self.get_id(&name) { + Ok(data) => data.clone(), + Err(err) => return Err(err), + }; + match self.get_id(&translation) { + Ok(_) => return Err(MTTError::NameDuplicate(translation)), + Err(_) => {} + } + let holder = self.ids.get_mut(&id).unwrap(); + holder.insert(translation.get_language().clone(), translation.clone()); + self.names.insert(translation, id); + Ok(id.clone()) + } + + fn get_name(&self, id: &Uuid, lang: &Language) -> Result { + match self.ids.get(id) { + Some(langdb) => match langdb.get(lang) { + Some(name) => Ok(name.clone()), + None => Err(MTTError::NameMissingTranslation(lang.clone())), + }, + None => Err(MTTError::NameInvalidID(id.clone())), + } + } + + fn get_id(&self, name: &Name) -> Result<&Uuid, MTTError> { + match self.names.get(name) { + Some(id) => Ok(id), + None => Err(MTTError::NameNotFound(name.clone())), + } + } +} + +#[cfg(test)] +mod names { + use super::*; + + #[test] + fn are_name_ids_unique() { + let mut names = Names::new(); + let data = ["one", "two", "three", "four", "five"]; + let mut ids: HashSet = HashSet::new(); + for item in data.iter() { + let name = Name::english(item.to_string()); + ids.insert(names.add_name(name).unwrap()); + } + assert_eq!(ids.len(), data.len()); + } + + #[test] + fn does_id_return_name() { + let mut names = Names::new(); + let data = ["one", "two"]; + let mut ids: HashMap = HashMap::new(); + for item in data.iter() { + let name = Name::english(item.to_string()); + ids.insert(name.clone(), names.add_name(name).unwrap()); + } + for (name, id) in ids.iter() { + assert_eq!( + &names + .get_name(id, &Language::from_639_1("en").unwrap()) + .unwrap(), + name + ); + assert_eq!(names.get_id(name).unwrap(), id); + } + } + + #[test] + fn errors_on_name_not_found() { + let mut names = Names::new(); + let name = Name::english("missing".to_string()); + let result = names.get_id(&name); + match result { + Ok(_) => unreachable!("got {:?}, should have been error", result), + Err(err) => match err { + MTTError::NameNotFound(output) => assert_eq!(output, name), + _ => unreachable!("got {:?}, should have been name not found", err), + }, + } + } + + #[test] + fn errors_on_bad_id() { + let mut names = Names::new(); + let id = Uuid::new_v4(); + let result = names.get_name(&id, &Language::from_639_1("en").unwrap()); + match result { + Ok(_) => unreachable!("got {:?}, should be invalid id error", result), + Err(err) => match err { + MTTError::NameInvalidID(data) => assert_eq!(data, id), + _ => unreachable!("got {:?}, should have been invalid id", err), + }, + } + } + + #[test] + fn errors_on_missing_translation() { + let mut names = Names::new(); + let name = Name::english("task".to_string()); + let lang = Language::from_639_1("ja").unwrap(); + let id = names.add_name(name).unwrap(); + let result = names.get_name(&id, &lang); + match result { + Ok(_) => unreachable!("got {:?}, should be invalid id error", result), + Err(err) => match err { + MTTError::NameMissingTranslation(data) => assert_eq!(data, lang), + _ => unreachable!("got {:?}, should have been invalid id", err), + }, + } + } + + #[test] + fn errors_on_duplicate_names() { + let mut names = Names::new(); + let data = "test".to_string(); + let name = Name::english(data.clone()); + let id = names.add_name(name.clone()); + let output = names.add_name(name.clone()); + match output { + Ok(_) => unreachable!( + "got {:?}, should have produced duplicate name error", + output + ), + Err(err) => match err { + MTTError::NameDuplicate(result) => assert_eq!(result, name), + _ => unreachable!("got {:?}, should have been duplicate name", err), + }, + } + } + + #[test] + fn allows_alternate_names() { + let mut names = Names::new(); + let data = "test".to_string(); + let alt = "テスト".to_string(); + let english = Name::english(data.clone()); + let japanese = Name::japanese(alt.clone()); + let id = names.add_name(english.clone()).unwrap(); + let result = names.add_translation(english, japanese.clone()).unwrap(); + assert_eq!(result, id); + println!("\n{:?}", names); + let output = names.get_name(&id, &Language::from_639_1("ja").unwrap()); + assert_eq!(output.unwrap().to_string(), alt); + assert_eq!(names.get_id(&japanese).unwrap(), &id); + } + + #[test] + fn errors_on_bad_translation() { + let mut names = Names::new(); + let data = "test".to_string(); + let alt = "テスト".to_string(); + let english = Name::english(data.clone()); + let japanese = Name::japanese(alt.clone()); + let result = names.add_translation(japanese.clone(), english); + match result { + Ok(_) => unreachable!("got {:?}, should be invalid id error", result), + Err(err) => match err { + MTTError::NameNotFound(output) => assert_eq!(output, japanese), + _ => unreachable!("got {:?}, should have been invalid id", err), + }, + } + } + + #[test] + fn errors_on_translation_duplicates() { + let mut names = Names::new(); + let data = "test".to_string(); + let alt = "テスト".to_string(); + let english = Name::english(data.clone()); + let japanese = Name::japanese(alt.clone()); + let id = names.add_name(english.clone()).unwrap(); + let id = names.add_name(japanese.clone()).unwrap(); + let result = names.add_translation(english, japanese.clone()); + match result { + Ok(_) => unreachable!( + "got {:?}, should have produced duplicate name error", + result + ), + Err(err) => match err { + MTTError::NameDuplicate(result) => assert_eq!(result, japanese), + _ => unreachable!("got {:?}, should have been duplicate name", err), + }, + } + } +} + #[derive(Clone, Debug)] enum RegMsg { + AddRoute(RouteRequest), DocName(Name), Error(MTTError), Ok, @@ -499,7 +732,7 @@ impl Register { sender_id: sender_id, } } - + fn get_msg(&self) -> &RegMsg { &self.msg } @@ -650,7 +883,7 @@ mod routes { } } -#[derive(Clone)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] struct RouteRequest { msg_id: Include, doc_name: Include, @@ -968,6 +1201,7 @@ struct DocRegistry { doc_names: Vec, queue: Queue, receiver: Receiver, + routes: HashMap, } impl DocRegistry { @@ -976,6 +1210,7 @@ impl DocRegistry { doc_names: Vec::new(), queue: queue, receiver: rx, + routes: HashMap::new(), } } @@ -994,8 +1229,8 @@ impl DocRegistry { let id = data.get_sender_id(); let reply = msg.response(self.register_action(data)); self.queue.forward(id, reply); - }, - _ => {}, + } + _ => {} } } } @@ -1004,12 +1239,19 @@ impl DocRegistry { match reg.get_msg() { RegMsg::DocName(name) => { if self.doc_names.contains(name) { - reg.response(RegMsg::Error(MTTError::DocumentAlreadyExists(name.to_string()))) + reg.response(RegMsg::Error(MTTError::DocumentAlreadyExists( + name.to_string(), + ))) } else { self.doc_names.push(name.clone()); reg.response(RegMsg::Ok) } - }, + } + RegMsg::AddRoute(route) => { + self.routes + .insert(route.clone(), reg.get_sender_id().clone()); + reg.response(RegMsg::Ok) + } _ => reg.response(RegMsg::Ok), } } @@ -1157,6 +1399,8 @@ mod queues { sender_id: Uuid, queue: Queue, receiver: Receiver, + doc_id: HashMap, + doc_rx: HashMap>, } impl TestQueue { @@ -1168,9 +1412,22 @@ mod queues { sender_id: id, queue: queue, receiver: rx, + doc_id: HashMap::new(), + doc_rx: HashMap::new(), } } + fn add_document(&mut self, name: String) { + let (tx, rx) = channel(); + let id = self.add_sender(tx); + let reg_msg = Register::new(id.clone(), RegMsg::DocName(Name::english(name.clone()))); + let msg = Message::new(NameID::None, reg_msg); + self.send(msg.clone()).unwrap(); + let result = rx.recv_timeout(TIMEOUT).unwrap(); + self.doc_id.insert(name.clone(), id); + self.doc_rx.insert(name.clone(), rx); + } + fn get_preset_id(&self) -> &Uuid { &self.sender_id } @@ -1179,6 +1436,14 @@ mod queues { &self.receiver } + fn get_doc_rx_id(&self, name: &str) -> &Uuid { + self.doc_id.get(name).unwrap() + } + + fn get_doc_rx(&self, name: &str) -> &Receiver { + self.doc_rx.get(name).unwrap() + } + fn add_sender(&mut self, sender: Sender) -> Uuid { self.queue.add_sender(sender) } @@ -1217,7 +1482,10 @@ mod queues { fn can_register_document_name() { let mut queue = TestQueue::new(); let doc_name = Name::english(Uuid::new_v4().to_string()); - let reg_msg = Register::new(queue.get_preset_id().clone(), RegMsg::DocName(doc_name.clone())); + let reg_msg = Register::new( + queue.get_preset_id().clone(), + RegMsg::DocName(doc_name.clone()), + ); let msg = Message::new(NameID::None, reg_msg); queue.send(msg.clone()).unwrap(); let result = queue.get_preset_rx().recv_timeout(TIMEOUT).unwrap(); @@ -1225,7 +1493,7 @@ mod queues { let action = result.get_action(); match action { MsgAction::Register(data) => match data.get_msg() { - RegMsg::Ok => {}, + RegMsg::Ok => {} _ => unreachable!("got {:?}, should have been register ok", action), }, _ => unreachable!("got {:?}, should have been register ok", action), @@ -1235,12 +1503,12 @@ mod queues { #[test] fn errors_on_duplicate_names() { let mut queue = TestQueue::new(); - //let mut queue = Queue::new(); - //let (sender, receiver) = channel(); - //let id = queue.add_sender(sender); let receiver = queue.get_preset_rx(); let doc_name = Name::english(Uuid::new_v4().to_string()); - let reg_msg = Register::new(queue.get_preset_id().clone(), RegMsg::DocName(doc_name.clone())); + let reg_msg = Register::new( + queue.get_preset_id().clone(), + RegMsg::DocName(doc_name.clone()), + ); let msg = Message::new(NameID::None, reg_msg.clone()); queue.send(msg.clone()).unwrap(); receiver.recv_timeout(TIMEOUT).unwrap(); @@ -1252,7 +1520,9 @@ mod queues { match action { MsgAction::Register(data) => match data.get_msg() { RegMsg::Error(err) => match err { - MTTError::DocumentAlreadyExists(name) => assert_eq!(name.to_string(), doc_name.to_string()), + MTTError::DocumentAlreadyExists(name) => { + assert_eq!(name.to_string(), doc_name.to_string()) + } _ => unreachable!("got {:?}, should have been duplicate error", err), }, _ => unreachable!("got {:?}, should have been error", data), @@ -1261,20 +1531,25 @@ mod queues { } } - #[test] - fn can_register_routes() { - - } - #[test] #[ignore] - fn default_send_does_nothing() { - let mut queue = Queue::new(); - let (sender, receiver) = channel(); - let id = queue.add_sender(sender); - let msg = Message::new("wiki", Query::new()); + fn can_register_routes() { + let mut queue = TestQueue::new(); + let names = ["task", "recipe"]; + for name in names.iter() { + queue.add_document(name.to_string()); + } + let route_req = RouteRequest::new(Include::All, Include::All, Include::All); + let reg_msg = RegMsg::AddRoute(route_req); + let reg = Register::new(queue.get_doc_rx_id(names[0]).clone(), reg_msg); + let msg = Message::new(NameID::None, reg); queue.send(msg).unwrap(); - match receiver.recv_timeout(TIMEOUT) { + queue.get_doc_rx(names[0]).recv_timeout(TIMEOUT).unwrap(); + let msg = Message::new(NameID::None, Query::new()); + queue.send(msg.clone()).unwrap(); + let result = queue.get_doc_rx(names[0]).recv_timeout(TIMEOUT).unwrap(); + assert_eq!(result.get_message_id(), msg.get_message_id()); + match queue.get_doc_rx(names[1]).recv_timeout(TIMEOUT) { Ok(msg) => unreachable!("should not receive: {:?}", msg), Err(err) => match err { RecvTimeoutError::Timeout => {}