use chrono::prelude::*; use isolang::Language; use std::{ collections::{HashMap, HashSet, VecDeque}, ops::{Add, AddAssign}, sync::{ mpsc::{channel, Receiver, Sender}, Arc, RwLock, }, thread::{sleep, spawn}, time::Duration, }; use uuid::Uuid; #[cfg(test)] mod support_test { use std::time::Duration; pub static TIMEOUT: Duration = Duration::from_millis(500); } #[derive(Clone, Debug)] enum MTTError { AdditionMissingField(Name), CannotConvertMessageToRouteID, DocumentAlreadyExists(String), DocumentFieldAlreadyExists(String, Field), DocumentFieldMissing(String), DocumentFieldNotFound(String), DocumentFieldWrongDataType(FieldType, FieldType), DocumentNotFound(String), FieldDuplicate, FieldInvalidType, FieldMissingData, InvalidNone, RecordMismatch, NameDuplicate(Name), NameInvalidID(Uuid), NameMissingTranslation(Language), NameNotFound(Name), QueryCannotChangeData, RouteRequiresDocumentID, } #[derive(Clone, Debug, Eq, Hash, PartialEq)] enum Action { Addition, Create, Delete, Error, GetLog, Log, OnAddition, OnDelete, OnQuery, OnUpdate, Query, Records, Register, Reply, Show, Update, } impl From for Action { fn from(value: MsgAction) -> Self { match value { MsgAction::Addition(_) => Action::Addition, MsgAction::Create(_) => Action::Create, MsgAction::Delete(_) => Action::Delete, MsgAction::Error(_) => Action::Error, MsgAction::GetLog(_) => Action::GetLog, MsgAction::Log(_) => Action::Log, MsgAction::OnAddition(_) => Action::OnAddition, MsgAction::OnDelete(_) => Action::OnDelete, MsgAction::OnQuery(_) => Action::OnQuery, MsgAction::OnUpdate(_) => Action::OnUpdate, MsgAction::Query(_) => Action::Query, MsgAction::Records(_) => Action::Records, MsgAction::Register(_) => Action::Register, MsgAction::Reply(_) => Action::Reply, MsgAction::Show => Action::Show, MsgAction::Update(_) => Action::Update, } } } impl From<&MsgAction> for Action { fn from(value: &MsgAction) -> Self { let action = value.clone(); Self::from(action) } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] enum NameID { ID(Uuid), Name(String), None, } impl NameID { fn is_none(&self) -> bool { match self { Self::None => true, _ => false, } } } impl From<&str> for NameID { fn from(value: &str) -> Self { Self::Name(value.to_string()) } } impl From for NameID { fn from(value: String) -> Self { Self::Name(value) } } impl From for NameID { fn from(value: Uuid) -> Self { Self::ID(value) } } impl From<&NameID> for NameID { fn from(value: &NameID) -> Self { value.clone() } } #[derive(Clone, Debug)] enum MsgAction { Addition(Addition), Create(DocDef), // Alter // Remove Error(MTTError), GetLog(Uuid), Log(Vec), OnAddition(Records), OnDelete(Records), OnQuery(Records), OnUpdate(Records), Query(Query), Records(Records), Register(Register), Reply(Reply), Show, Delete(Delete), Update(Update), } impl From for MsgAction { fn from(value: Addition) -> Self { MsgAction::Addition(value) } } impl From for MsgAction { fn from(value: Delete) -> Self { MsgAction::Delete(value) } } impl From for MsgAction { fn from(value: DocDef) -> Self { MsgAction::Create(value) } } impl From for MsgAction { fn from(value: MTTError) -> Self { MsgAction::Error(value) } } impl From for MsgAction { fn from(value: Query) -> Self { MsgAction::Query(value) } } impl From for MsgAction { fn from(value: Records) -> Self { MsgAction::Records(value) } } impl From for MsgAction { fn from(value: Register) -> Self { MsgAction::Register(value) } } impl From for MsgAction { fn from(value: Reply) -> Self { MsgAction::Reply(value) } } impl From for MsgAction { fn from(value: Update) -> Self { MsgAction::Update(value) } } impl From for MsgAction { fn from(value: Uuid) -> Self { MsgAction::GetLog(value) } } impl From<&Uuid> for MsgAction { fn from(value: &Uuid) -> Self { Self::from(value.clone()) } } #[cfg(test)] mod msgactions { use super::*; #[test] fn turn_document_definition_into_action() { let name = Name::english(Uuid::new_v4().to_string().as_str()); let value = DocDef::new(name.clone()); let result: MsgAction = value.into(); match result { MsgAction::Create(def) => assert_eq!(def.get_document_names(), &[name].to_vec()), _ => unreachable!("Got {:?}: dhould have been create", result), } } #[test] fn turn_error_into_action() { let data = "data".to_string(); let value = MTTError::DocumentAlreadyExists(data.clone()); let result: MsgAction = value.into(); match result { MsgAction::Error(result) => match result { MTTError::DocumentAlreadyExists(output) => assert_eq!(output, data), _ => unreachable!("Got {:?}: dhould have been create", result), }, _ => unreachable!("Got {:?}: dhould have been create", result), } let value = MTTError::DocumentNotFound(data.clone()); let result: MsgAction = value.into(); match result { MsgAction::Error(result) => match result { MTTError::DocumentNotFound(output) => assert_eq!(output, data), _ => unreachable!("Got {:?}: dhould have been create", result), }, _ => unreachable!("Got {:?}: dhould have been create", result), } } #[test] fn turn_query_into_action() { let value = Query::new(); let result: MsgAction = value.into(); match result { MsgAction::Query(_) => {} _ => unreachable!("Got {:?}: dhould have been query", result), } } #[test] fn turn_reply_into_action() { let value = Reply::new(); let result: MsgAction = value.into(); match result { MsgAction::Reply(_) => {} _ => unreachable!("Got {:?}: dhould have been reply", result), } } } #[derive(Clone, Debug)] struct Message { msg_id: Uuid, document_id: NameType, action: MsgAction, // session: Option } impl Message { fn new(doc_id: D, action: A) -> Self where D: Into, A: Into, { Self { msg_id: Uuid::new_v4(), document_id: doc_id.into(), action: action.into(), } } fn get_message_id(&self) -> &Uuid { &self.msg_id } fn get_document_id(&self) -> &NameType { &self.document_id } fn get_action(&self) -> &MsgAction { &self.action } fn get_path(&self) -> Path { Path::new( Include::Some(self.msg_id.clone()), Include::Some(self.document_id.clone()), Include::Some(self.action.clone().into()), ) } fn reset_name_id(&mut self, name: NT) where NT: Into, { self.document_id = name.into(); } fn response(&self, action: A) -> Self where A: Into, { Self { msg_id: self.msg_id.clone(), document_id: self.document_id.clone(), action: action.into(), } } } #[cfg(test)] mod messages { use super::*; #[test] fn can_the_document_be_a_stringi_reference() { let dts = [Name::english("one"), Name::english("two")]; for document in dts.into_iter() { let msg = Message::new( document.clone(), MsgAction::Create(DocDef::new(document.clone())), ); match msg.get_document_id() { NameType::Name(data) => assert_eq!(data, &document), _ => unreachable!("should have been a string id"), } match msg.get_action() { MsgAction::Create(_) => {} _ => unreachable!("should have been a create document"), } } } #[test] fn can_the_document_be_an_id() { let document = Uuid::new_v4(); let msg = Message::new(document.clone(), Query::new()); match msg.get_document_id() { NameType::ID(data) => assert_eq!(data, &document), _ => unreachable!("should have been an id"), } match msg.get_action() { MsgAction::Query(_) => {} _ => unreachable!("should have been an access query"), } } #[test] fn is_the_message_id_random() { let mut ids: Vec = Vec::new(); for _ in 0..5 { let msg = Message::new(Name::english("tester"), Query::new()); let id = msg.get_message_id().clone(); assert!(!ids.contains(&id), "{:?} containts {}", ids, id); ids.push(id); } } #[test] fn Can_make_reply_message() { let name = Name::english("testing"); let msg = Message::new(name.clone(), Query::new()); let responce = Reply::new(); let reply = msg.response(responce); assert_eq!(reply.get_message_id(), msg.get_message_id()); match reply.get_document_id() { NameType::Name(data) => assert_eq!(data, &name), _ => unreachable!("should have been a name"), } match reply.get_action() { MsgAction::Reply(_) => {} _ => unreachable!("should have been a reply"), } } #[test] fn Can_make_error_message() { let name = Name::english("testing"); let msg = Message::new(name.clone(), Query::new()); let err_msg = Uuid::new_v4().to_string(); let result = msg.response(MTTError::DocumentNotFound(err_msg.clone())); assert_eq!(result.get_message_id(), msg.get_message_id()); match result.get_document_id() { NameType::Name(data) => assert_eq!(data, &name), _ => unreachable!("should have been a name"), } match result.get_action() { MsgAction::Error(data) => match data { MTTError::DocumentNotFound(txt) => assert_eq!(txt, &err_msg), _ => unreachable!("got {:?}, should have received not found", data), }, _ => unreachable!("should have been a reply"), } } #[test] fn can_make_a_response_message() { let doc_id = Uuid::new_v4(); let msg = Message::new(doc_id.clone(), Query::new()); let data = Uuid::new_v4().to_string(); let result1 = msg.response(MTTError::DocumentNotFound(data.clone())); let result2 = msg.response(Reply::new()); assert_eq!(result1.get_message_id(), msg.get_message_id()); assert_eq!(result2.get_message_id(), msg.get_message_id()); assert_eq!(result1.get_document_id(), msg.get_document_id()); assert_eq!(result2.get_document_id(), msg.get_document_id()); let action1 = result1.get_action(); match action1 { MsgAction::Error(err) => match err { MTTError::DocumentNotFound(output) => assert_eq!(output, &data), _ => unreachable!("got {:?}: should have received document not found", err), }, _ => unreachable!("got {:?}: should have received error", action1), } let action2 = result2.get_action(); match action2 { MsgAction::Reply(data) => assert_eq!(data.len(), 0), _ => unreachable!("got {:?}: should have received a reply", action2), } } #[test] fn can_reset_document_id() { let mut msg = Message::new(Name::english("something"), Query::new()); let id = Uuid::new_v4(); msg.reset_name_id(&id); let name_id = msg.get_document_id(); match name_id { NameType::ID(data) => assert_eq!(data, &id), _ => unreachable!("got {:?}, should have been an id", name_id), } } } #[derive(Clone, Debug, Eq, Hash)] enum Include { All, Some(T), } impl PartialEq for Include { fn eq(&self, other: &Self) -> bool { match self { Include::All => true, Include::Some(data) => match other { Include::All => true, Include::Some(other_data) => data == other_data, }, } } } #[cfg(test)] mod includes { use super::*; #[test] fn does_all_equal_evberything() { let a: Include = Include::All; let b: Include = Include::Some(5); let c: Include = Include::Some(7); assert!(a == a, "all should equal all"); assert!(a == b, "all should equal some"); assert!(b == a, "some should equal all"); assert!(b == b, "same some should equal"); assert!(b != c, "different somes do not equal"); } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] struct RouteID { action: Option, doc_type: Option, msg_id: Option, } impl From for RouteID { fn from(value: Route) -> Self { Self { action: match value.action { Include::All => None, Include::Some(action) => Some(action.clone()), }, doc_type: match value.doc_type { Include::All => None, Include::Some(doc) => Some(doc.clone()), }, msg_id: match value.msg_id { Include::All => None, Include::Some(id) => Some(id.clone()), }, } } } impl TryFrom for RouteID { type Error = MTTError; fn try_from(value: Message) -> Result { let doc_id = match value.get_document_id() { NameType::ID(data) => data, _ => return Err(MTTError::CannotConvertMessageToRouteID), }; Ok(RouteID { action: Some(value.get_action().into()), doc_type: Some(doc_id.clone()), msg_id: Some(value.get_message_id().clone()), }) } } #[cfg(test)] mod route_ids { use super::*; #[test] fn can_get_message_route_id() { let actions: Vec = vec![Query::new().into(), MsgAction::Show]; let doc_id = Uuid::new_v4(); for action in actions.iter() { let msg = Message::new(doc_id.clone(), action.clone()); let route_id: RouteID = msg.clone().try_into().unwrap(); match route_id.msg_id { Some(data) => assert_eq!(&data, msg.get_message_id()), None => unreachable!("should have had a message id"), } match route_id.doc_type { Some(data) => assert_eq!(data, doc_id), None => unreachable!("should have had doc type"), } match route_id.action { Some(data) => assert_eq!(data, action.into()), None => unreachable!("should have had doc type"), } } } #[test] fn errors_when_doc_id_is_not_id() { let msg = Message::new(Name::english("nope"), Query::new()); match TryInto::::try_into(msg) { Ok(_) => unreachable!("should be an error"), Err(err) => match err { MTTError::CannotConvertMessageToRouteID => {} _ => unreachable!("got {:?}, should have been covert error", err), }, } } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] enum NameType { ID(Uuid), Name(Name), None, } impl From<&NameType> for NameType { fn from(value: &NameType) -> Self { value.clone() } } impl From for NameType { fn from(value: Name) -> Self { Self::Name(value) } } impl From<&Name> for NameType { fn from(value: &Name) -> Self { let name = value.clone(); Self::from(name) } } impl From for NameType { fn from(value: Uuid) -> Self { Self::ID(value) } } impl From<&Uuid> for NameType { fn from(value: &Uuid) -> Self { let id = value.clone(); Self::from(id) } } impl ToString for NameType { fn to_string(&self) -> String { match self { Self::ID(data) => data.to_string(), Self::Name(data) => data.to_string(), Self::None => "'{None}'".to_string(), } } } #[derive(Clone, Debug)] struct Path { msg_id: Include, doc: Include, action: Include, } impl Path { fn new(id: Include, doc: Include, action: Include) -> Self { Self { msg_id: id, doc: doc, action: action, } } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] struct Name { name: String, lang: Language, } impl Name { fn new(name: &str, lang: Language) -> Self { Self { name: name.to_lowercase(), lang: lang, } } fn get_language(&self) -> &Language { &self.lang } fn english(name: &str) -> Self { Self::new(name, Language::from_639_1("en").unwrap()) } fn japanese(name: &str) -> Self { Self::new(name, Language::from_639_1("ja").unwrap()) } } impl ToString for Name { fn to_string(&self) -> String { self.name.clone() } } #[derive(Clone, Debug, PartialEq)] struct Names { names: HashMap, ids: HashMap>, } impl Names { fn new() -> Self { Self { names: HashMap::new(), ids: HashMap::new(), } } fn add_names(&mut self, names: Vec) -> Result { for name in names.iter() { if self.names.contains_key(&name) { return Err(MTTError::NameDuplicate(name.clone())); } } let mut id = Uuid::new_v4(); while self.ids.contains_key(&id) { id = Uuid::new_v4(); } for name in names.iter() { self.names.insert(name.clone(), id.clone()); let mut holder: HashMap = HashMap::new(); holder.insert(name.get_language().clone(), name.clone()); 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: NT) -> Result where NT: Into, { match name.into() { NameType::Name(data) => match self.names.get(&data) { Some(id) => Ok(id.clone()), None => Err(MTTError::NameNotFound(data.clone())), }, NameType::ID(data) => { if self.ids.contains_key(&data) { Ok(data) } else { if data == Uuid::nil() { Ok(data) } else { Err(MTTError::NameNotFound(Name::english( data.to_string().as_str(), ))) } } } NameType::None => Ok(Uuid::nil()), } } fn path_to_route(&self, path: &Path) -> Result { let doc_id = match &path.doc { Include::Some(id_info) => match id_info { NameType::ID(id) => { if self.ids.contains_key(&id) { Include::Some(id.clone()) } else { return Err(MTTError::NameInvalidID(id.clone())); } } NameType::Name(name) => { let id = match self.get_id(name) { Ok(data) => data, Err(err) => return Err(err), }; Include::Some(id.clone()) } NameType::None => Include::Some(Uuid::nil()), }, Include::All => Include::All, }; Ok(Route::new(path.msg_id.clone(), doc_id, path.action.clone())) } fn is_empty(&self) -> bool { self.names.is_empty() } } #[cfg(test)] mod names { use super::*; #[test] fn are_names_lowercase() { let name1 = Name::new("session", Language::from_639_1("en").unwrap()); let name2 = Name::new("Session", Language::from_639_1("en").unwrap()); let name3 = Name::english("SESSION"); assert_eq!(name1, name2); assert_eq!(name1, name3); } #[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.clone()); ids.insert(names.add_names([name].to_vec()).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.clone()); ids.insert(name.clone(), names.add_names([name].to_vec()).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"); 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"); let lang = Language::from_639_1("ja").unwrap(); let id = names.add_names([name].to_vec()).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"; let name = Name::english(data); let id = names.add_names([name.clone()].to_vec()); let output = names.add_names([name.clone()].to_vec()); 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"; let alt = "テスト"; let english = Name::english(data); let japanese = Name::japanese(alt); let id = names.add_names([english.clone()].to_vec()).unwrap(); let result = names.add_translation(english, japanese.clone()).unwrap(); assert_eq!(result, id); 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"; let alt = "テスト"; let english = Name::english(data); let japanese = Name::japanese(alt); 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"; let alt = "テスト"; let english = Name::english(data); let japanese = Name::japanese(alt); let id = names.add_names([english.clone()].to_vec()).unwrap(); let id = names.add_names([japanese.clone()].to_vec()).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), }, } } #[test] fn convert_path_to_route_with_ids() { let mut names = Names::new(); let data = "data"; let english = Name::english(data); let id = names.add_names([english.clone()].to_vec()).unwrap(); let msg_id = Uuid::new_v4(); let action = Action::Query; let path = Path::new( Include::Some(msg_id.clone()), Include::Some(id.into()), Include::Some(action.clone()), ); let result = names.path_to_route(&path).unwrap(); assert_eq!(result.msg_id, Include::Some(msg_id)); assert_eq!(result.doc_type, Include::Some(id)); assert_eq!(result.action, Include::Some(action)); } #[test] fn convert_path_name_to_route() { let mut names = Names::new(); let data = "data"; let english = Name::english(data); let id = names.add_names([english.clone()].to_vec()).unwrap(); let msg_id = Uuid::new_v4(); let action = Action::Error; let path = Path::new( Include::Some(msg_id.clone()), Include::Some(english.into()), Include::Some(action.clone()), ); let result = names.path_to_route(&path).unwrap(); assert_eq!(result.msg_id, Include::Some(msg_id)); assert_eq!(result.doc_type, Include::Some(id)); assert_eq!(result.action, Include::Some(action)); } #[test] fn convert_path_with_no_document_to_route() { let mut names = Names::new(); let msg_id = Uuid::new_v4(); let action = Action::Show; let path = Path::new( Include::Some(msg_id.clone()), Include::Some(NameType::None), Include::Some(action.clone()), ); let result = names.path_to_route(&path).unwrap(); assert_eq!(result.msg_id, Include::Some(msg_id)); assert_eq!(result.doc_type, Include::Some(Uuid::nil())); assert_eq!(result.action, Include::Some(action)); } #[test] fn convert_path_to_route_all_documents() { let mut names = Names::new(); let msg_id = Uuid::new_v4(); let action = Action::Query; let path = Path::new( Include::Some(msg_id.clone()), Include::All, Include::Some(action.clone()), ); let result = names.path_to_route(&path).unwrap(); assert_eq!(result.msg_id, Include::Some(msg_id)); match result.doc_type { Include::All => {} Include::Some(_) => unreachable!("should return all"), } assert_eq!(result.action, Include::Some(action)); } #[test] fn convert_path_with_bad_id() { let mut names = Names::new(); let msg_id = Uuid::new_v4(); let id = Uuid::new_v4(); let action = Action::Query; let path = Path::new( Include::Some(msg_id.clone()), Include::Some(id.into()), Include::Some(action.clone()), ); match names.path_to_route(&path) { Ok(data) => unreachable!("got {:?}, should have been an error", data), Err(err) => match err { MTTError::NameInvalidID(output) => assert_eq!(output, id), _ => unreachable!("got {:?}, should have gotten invalid id", err), }, } } #[test] fn convert_path_with_bad_name() { let mut names = Names::new(); let msg_id = Uuid::new_v4(); let name = Name::english("wrong"); let action = Action::Query; let path = Path::new( Include::Some(msg_id.clone()), Include::Some(name.clone().into()), Include::Some(action.clone()), ); match names.path_to_route(&path) { Ok(data) => unreachable!("got {:?}, should have been an error", data), Err(err) => match err { MTTError::NameNotFound(output) => assert_eq!(output, name), _ => unreachable!("got {:?}, should have gotten invalid id", err), }, } } } #[derive(Clone, Debug)] enum RegMsg { AddRoute(Path), AddDocName(Vec), DocumentNameID(Uuid), Error(MTTError), GetNameID(Name), Ok, RouteID(RouteID), } #[derive(Clone, Debug)] struct Register { msg: RegMsg, sender_id: Uuid, } impl Register { fn new(sender_id: Uuid, reg_msg: RegMsg) -> Self { Self { msg: reg_msg, sender_id: sender_id, } } fn get_msg(&self) -> &RegMsg { &self.msg } fn get_sender_id(&self) -> &Uuid { &self.sender_id } fn response(&self, reg_msg: RegMsg) -> Self { Self { msg: reg_msg, sender_id: self.sender_id.clone(), } } } #[derive(Clone, Debug, PartialEq)] struct Route { action: Include, doc_type: Include, msg_id: Include, } impl Route { fn new(msg_id: Include, doc: Include, action: Include) -> Self { Self { action: action, doc_type: doc, msg_id: msg_id, } } } impl From for Route { fn from(value: RouteID) -> Self { Self { action: match value.action { Some(data) => Include::Some(data.clone()), None => Include::All, }, doc_type: match value.doc_type { Some(doc) => Include::Some(doc.clone()), None => Include::All, }, msg_id: match value.msg_id { Some(msg) => Include::Some(msg.clone()), None => Include::All, }, } } } impl From<&RouteID> for Route { fn from(value: &RouteID) -> Self { Self { action: match &value.action { Some(data) => Include::Some(data.clone()), None => Include::All, }, doc_type: match &value.doc_type { Some(doc) => Include::Some(doc.clone()), None => Include::All, }, msg_id: match &value.msg_id { Some(msg) => Include::Some(msg.clone()), None => Include::All, }, } } } impl TryFrom for Route { type Error = MTTError; fn try_from(value: Path) -> Result { let doc = match value.doc { Include::Some(data) => match data { NameType::ID(id) => Include::Some(id.clone()), _ => return Err(MTTError::RouteRequiresDocumentID), }, Include::All => Include::All, }; Ok(Self { action: value.action, doc_type: doc, msg_id: value.msg_id, }) } } #[cfg(test)] mod routes { use super::*; #[test] fn can_a_route_set_action() { let actions = [Action::Query, Action::Reply]; for action in actions.into_iter() { let route = Route::new(Include::All, Include::All, Include::Some(action.clone())); match route.msg_id { Include::All => {} Include::Some(_) => unreachable!("should have been all"), } match route.doc_type { Include::All => {} Include::Some(_) => unreachable!("should have been all"), } match route.action { Include::All => unreachable!("should be a specific value"), Include::Some(result) => assert_eq!(result, action), } } } #[test] fn can_route_set_document_by_name() { let doc_id = Uuid::new_v4(); let route = Route::new(Include::All, Include::Some(doc_id.clone()), Include::All); match route.msg_id { Include::All => {} Include::Some(_) => unreachable!("should have been all"), } match route.doc_type { Include::All => unreachable!("should be a specific value"), Include::Some(result) => assert_eq!(result, doc_id), } match route.action { Include::All => {} Include::Some(_) => unreachable!("should have been all"), } } #[test] fn can_route_set_document_by_id() { let id = Uuid::new_v4(); let route = Route::new(Include::All, Include::Some(id.clone()), Include::All); match route.msg_id { Include::All => {} Include::Some(_) => unreachable!("should have been all"), } match route.doc_type { Include::All => unreachable!("should be a specific value"), Include::Some(result) => assert_eq!(result, id), } match route.action { Include::All => {} Include::Some(_) => unreachable!("should have been all"), } } #[test] fn can_route_be_set_by_message_id() { let id = Uuid::new_v4(); let route = Route::new(Include::Some(id.clone()), Include::All, Include::All); match route.msg_id { Include::All => unreachable!("should be a specific value"), Include::Some(result) => assert_eq!(result, id), } match route.doc_type { Include::All => {} Include::Some(_) => unreachable!("should have been all"), } match route.action { Include::All => {} Include::Some(_) => unreachable!("should have been all"), } } } #[derive(Clone, Debug, Eq, PartialEq)] struct RouteRequest { msg_id: Include, doc_name: Include, action: Include, } impl RouteRequest { fn new(msg_id: Include, doc_name: Include, action: Include) -> Self { Self { msg_id: msg_id, doc_name: doc_name, action: action, } } } struct DocRegistry { doc_names: Names, queue: Queue, receiver: Receiver, routes: HashMap>, } impl DocRegistry { fn new(queue: Queue, rx: Receiver) -> Self { Self { doc_names: Names::new(), queue: queue, receiver: rx, routes: HashMap::new(), } } fn start(queue: Queue, rx: Receiver) { let mut doc_names = DocRegistry::new(queue, rx); spawn(move || { doc_names.listen(); }); } fn listen(&mut self) { loop { let mut msg = self.receiver.recv().unwrap(); match msg.get_action() { MsgAction::Register(data) => { let id = data.get_sender_id(); let reply = msg.response(self.register_action(data)); self.queue.forward(id, reply); } _ => match self.doc_names.get_id(msg.get_document_id()) { Ok(doc_id) => { msg.reset_name_id(doc_id); let route: Route = msg.get_path().try_into().unwrap(); let mut send_to: HashSet = HashSet::new(); for (route_id, senders) in self.routes.iter() { if route == route_id.into() { send_to = send_to.union(senders).cloned().collect(); } } for send_id in send_to.iter() { self.queue.forward(send_id, msg.clone()); } } Err(err) => self .queue .send(msg.response(MsgAction::Error(err))) .unwrap(), }, } } } fn register_action(&mut self, reg: &Register) -> Register { match reg.get_msg() { RegMsg::AddDocName(names) => match self.doc_names.add_names(names.clone()) { Ok(id) => reg.response(RegMsg::DocumentNameID(id.clone())), Err(err) => reg.response(RegMsg::Error(err)), }, RegMsg::AddRoute(path) => { let route = self.doc_names.path_to_route(path).unwrap(); let route_id: RouteID = route.into(); let senders = match self.routes.get_mut(&route_id) { Some(ids) => ids, None => { self.routes.insert(route_id.clone(), HashSet::new()); self.routes.get_mut(&route_id).unwrap() } }; senders.insert(reg.get_sender_id().clone()); reg.response(RegMsg::RouteID(route_id)) } RegMsg::GetNameID(name) => match self.doc_names.get_id(name) { Ok(id) => reg.response(RegMsg::DocumentNameID(id.clone())), Err(err) => reg.response(RegMsg::Error(err)), }, _ => reg.response(RegMsg::Ok), } } } struct Router { doc_registry: Sender, senders: HashMap>, } impl Router { fn new(tx: Sender) -> Self { Self { doc_registry: tx, senders: HashMap::new(), } } fn add_sender(&mut self, sender: Sender) -> Uuid { let mut id = Uuid::new_v4(); while self.senders.contains_key(&id) { id = Uuid::new_v4(); } self.senders.insert(id.clone(), sender); id } fn remove_sender(&mut self, id: &Uuid) { self.senders.remove(id); } fn forward(&self, id: &Uuid, msg: Message) { self.senders.get(id).unwrap().send(msg).unwrap(); } fn send(&self, msg: Message) { self.doc_registry.send(msg).unwrap(); } } #[cfg(test)] mod routers { use super::{support_test::TIMEOUT, *}; #[test] fn can_pass_message() { let (tx, rx) = channel(); let router = Router::new(tx); let msg = Message::new(Name::english("task"), Query::new()); router.send(msg.clone()); let result = rx.recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); } #[test] fn can_forward_message() { let (tx, _) = channel(); let mut router = Router::new(tx); let (sender, receiver) = channel(); let id = router.add_sender(sender); let msg = Message::new(Name::english("wiki"), Query::new()); router.forward(&id, msg.clone()); let result = receiver.recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); } #[test] fn sender_ids_are_unique() { let (tx, _) = channel(); let mut router = Router::new(tx); let count = 10; let mut holder: HashSet = HashSet::new(); for _ in 0..count { let (tx, _) = channel(); holder.insert(router.add_sender(tx)); } assert_eq!(holder.len(), count, "had duplicate keys"); } #[test] fn can_remove_sender() { let (tx, _) = channel(); let mut router = Router::new(tx); let (data, _) = channel(); let id = router.add_sender(data); assert_eq!(router.senders.len(), 1, "should have only one sender"); router.remove_sender(&id); assert_eq!(router.senders.len(), 0, "should have no senders."); } #[test] fn ignores_bad_id_removals() { let (tx, _) = channel(); let mut router = Router::new(tx); router.remove_sender(&Uuid::new_v4()); assert_eq!(router.senders.len(), 0, "should have no senders."); } } #[derive(Clone)] struct Queue { router: Arc>, } impl Queue { fn new() -> Self { let (tx, rx) = channel(); let output = Self { router: Arc::new(RwLock::new(Router::new(tx))), }; DocRegistry::start(output.clone(), rx); output } fn add_sender(&mut self, sender: Sender) -> Uuid { let mut router = self.router.write().unwrap(); router.add_sender(sender) } fn remove_sender(&mut self, id: &Uuid) { let mut router = self.router.write().unwrap(); router.remove_sender(id); } fn forward(&self, id: &Uuid, msg: Message) { let router = self.router.read().unwrap(); router.forward(id, msg); } fn send(&self, msg: Message) -> Result<(), MTTError> { let router = self.router.read().unwrap(); router.send(msg.clone()); Ok(()) } } #[cfg(test)] mod queues { use super::{support_test::TIMEOUT, *}; use std::sync::mpsc::RecvTimeoutError; struct TestQueue { sender_id: Uuid, queue: Queue, receiver: Receiver, doc_names: HashMap, doc_tx_id: HashMap, doc_rx: HashMap>, } impl TestQueue { fn new() -> Self { let mut queue = Queue::new(); let (tx, rx) = channel(); let id = queue.add_sender(tx); Self { sender_id: id, queue: queue, receiver: rx, doc_names: HashMap::new(), doc_tx_id: HashMap::new(), doc_rx: HashMap::new(), } } fn add_document(&mut self, name: Name) { let (tx, rx) = channel(); let id = self.queue.add_sender(tx); let reg_msg = Register::new(id.clone(), RegMsg::AddDocName([name.clone()].to_vec())); let msg = Message::new(NameType::None, reg_msg); self.queue.send(msg.clone()).unwrap(); match rx.recv_timeout(TIMEOUT).unwrap().get_action() { MsgAction::Register(doc_data) => match doc_data.get_msg() { RegMsg::DocumentNameID(data) => { self.doc_names.insert(name.clone(), data.clone()) } _ => panic!("should not get here"), }, _ => panic!("should not get here"), }; self.doc_tx_id.insert(name.clone(), id); self.doc_rx.insert(name.clone(), rx); } fn get_preset_id(&self) -> &Uuid { &self.sender_id } fn get_preset_rx(&self) -> &Receiver { &self.receiver } fn get_doc_id(&self, name: &Name) -> &Uuid { self.doc_names.get(name).unwrap() } fn get_doc_rx_id(&self, name: &Name) -> &Uuid { self.doc_tx_id.get(name).unwrap() } fn get_doc_rx(&self, name: &Name) -> &Receiver { self.doc_rx.get(name).unwrap() } fn get_queue(&self) -> Queue { self.queue.clone() } } #[test] fn can_forward_message() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); let msg = Message::new(Name::english("wiki"), Query::new()); queue.forward(tester.get_preset_id(), msg.clone()); let result = tester.get_preset_rx().recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); } #[test] fn sender_ids_are_unique() { let mut queue = Queue::new(); let count = 10; let mut holder: HashSet = HashSet::new(); for _ in 0..count { let (tx, _) = channel(); holder.insert(queue.add_sender(tx)); } assert_eq!(holder.len(), count, "had duplicate keys"); let router = queue.router.read().unwrap(); assert_eq!( router.senders.len(), count, "should contain all of the senders" ); } #[test] fn senders_can_be_removed() { let mut queue = Queue::new(); let (tx, _) = channel(); let id = queue.add_sender(tx); queue.remove_sender(&id); let router = queue.router.read().unwrap(); assert_eq!(router.senders.len(), 0, "should contain no senders"); } #[test] fn document_names_have_unique_id() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); let names = [ Name::english("one"), Name::english("two"), Name::english("three"), Name::english("four"), Name::english("five"), ]; let mut ids: Vec = Vec::new(); for name in names.iter() { let reg_msg = Register::new( tester.get_preset_id().clone(), RegMsg::AddDocName([name.clone()].to_vec()), ); let msg = Message::new(NameType::None, reg_msg); queue.send(msg.clone()).unwrap(); let result = tester.get_preset_rx().recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); let action = result.get_action(); match action { MsgAction::Register(data) => match data.get_msg() { RegMsg::DocumentNameID(data) => { assert!(!ids.contains(data), "{} already in {:?}", data, ids); ids.push(data.clone()); } _ => unreachable!("got {:?}, should have been register ok", action), }, _ => unreachable!("got {:?}, should have been register ok", action), } } } #[test] fn does_name_id_get_updated() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); let doc_name = Name::english("test"); let reg_msg = Register::new( tester.get_preset_id().clone(), RegMsg::AddDocName([doc_name.clone()].to_vec()), ); let msg = Message::new(NameType::None, reg_msg); queue.send(msg.clone()).unwrap(); let result = tester.get_preset_rx().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); let id = match action { MsgAction::Register(data) => match data.get_msg() { RegMsg::DocumentNameID(data) => data.clone(), _ => unreachable!("got {:?}, should have been register ok", action), }, _ => unreachable!("got {:?}, should have been register ok", action), }; let reg_msg = Register::new( tester.get_preset_id().clone(), RegMsg::AddRoute(Path::new(Include::All, Include::All, Include::All)), ); let msg = Message::new(NameType::None, reg_msg); queue.send(msg.clone()).unwrap(); tester.get_preset_rx().recv_timeout(TIMEOUT).unwrap(); let msg = Message::new(doc_name.clone(), Query::new()); queue.send(msg.clone()).unwrap(); let result = tester.get_preset_rx().recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); let name_id: NameType = id.into(); assert_eq!(result.get_document_id(), &name_id); } #[test] fn can_register_multiple_names_at_once() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); let names = [Name::english("one"), Name::japanese("一")].to_vec(); let reg_msg = Register::new( tester.get_preset_id().clone(), RegMsg::AddDocName(names.clone()), ); let msg = Message::new(NameType::None, reg_msg); queue.send(msg.clone()).unwrap(); let result = tester.get_preset_rx().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); let id = match action { MsgAction::Register(data) => match data.get_msg() { RegMsg::DocumentNameID(data) => data, _ => unreachable!("got {:?}, should have returned id", data), }, _ => unreachable!("got {:?}, should have returned id", action), }; for name in names.iter() { let reg_msg = Register::new( tester.get_preset_id().clone(), RegMsg::GetNameID(name.clone()), ); let msg = Message::new(NameType::None, reg_msg); queue.send(msg.clone()).unwrap(); let result = tester.get_preset_rx().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); let result = match action { MsgAction::Register(data) => match data.get_msg() { RegMsg::DocumentNameID(data) => data, _ => unreachable!("got {:?}, should have returned id", data), }, _ => unreachable!("got {:?}, should have returned id", action), }; assert_eq!(result, id); } } #[test] fn errors_on_duplicate_names() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); let receiver = tester.get_preset_rx(); let doc_name = Name::english(Uuid::new_v4().to_string().as_str()); let reg_msg = Register::new( tester.get_preset_id().clone(), RegMsg::AddDocName([doc_name.clone()].to_vec()), ); let msg = Message::new(NameType::None, reg_msg.clone()); queue.send(msg.clone()).unwrap(); receiver.recv_timeout(TIMEOUT).unwrap(); let msg2 = Message::new(NameType::None, reg_msg.clone()); queue.send(msg2.clone()).unwrap(); let result = receiver.recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg2.get_message_id()); let action = result.get_action(); match action { MsgAction::Register(data) => match data.get_msg() { RegMsg::Error(err) => match err { MTTError::NameDuplicate(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), }, _ => unreachable!("got {:?}, should have been register ok", action), } } #[test] fn can_register_routes() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); let names = [Name::english("task"), Name::english("recipe")]; for name in names.iter() { tester.add_document(name.clone()); } let route_req = Path::new(Include::All, Include::All, Include::All); let reg_msg = RegMsg::AddRoute(route_req); let reg = Register::new(tester.get_doc_rx_id(&names[0]).clone(), reg_msg); let msg = Message::new(NameType::None, reg); queue.send(msg).unwrap(); tester.get_doc_rx(&names[0]).recv_timeout(TIMEOUT).unwrap(); let msg = Message::new(NameType::None, Query::new()); queue.send(msg.clone()).unwrap(); let result = tester.get_doc_rx(&names[0]).recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); match tester.get_doc_rx(&names[1]).recv_timeout(TIMEOUT) { Ok(msg) => unreachable!("should not receive: {:?}", msg), Err(err) => match err { RecvTimeoutError::Timeout => {} _ => unreachable!("should have timed out"), }, } } #[test] fn can_multiple_register_for_the_same_route() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); let names = [Name::english("task"), Name::english("recipe")]; let route_req = Path::new(Include::All, Include::All, Include::All); let reg_msg = RegMsg::AddRoute(route_req); for name in names.iter() { tester.add_document(name.clone()); let reg = Register::new(tester.get_doc_rx_id(name).clone(), reg_msg.clone()); let msg = Message::new(NameType::None, reg); queue.send(msg).unwrap(); tester.get_doc_rx(name).recv_timeout(TIMEOUT).unwrap(); } let msg = Message::new(NameType::None, Query::new()); queue.send(msg.clone()).unwrap(); for name in names.iter() { let result = tester.get_doc_rx(name).recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); } } #[test] fn does_receiver_only_receives_the_message_once() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); let name = Name::english("something"); tester.add_document(name.clone()); let paths = [ Path::new(Include::All, Include::All, Include::All), Path::new( Include::All, Include::Some(name.clone().into()), Include::All, ), ]; for path in paths.iter() { let reg_msg = RegMsg::AddRoute(path.clone()); let reg = Register::new(tester.get_doc_rx_id(&name).clone(), reg_msg); let msg = Message::new(NameType::None, reg); queue.send(msg).unwrap(); tester.get_doc_rx(&name).recv_timeout(TIMEOUT).unwrap(); } let msg = Message::new(name.clone(), Query::new()); queue.send(msg.clone()).unwrap(); let result = tester.get_doc_rx(&name).recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); match tester.get_doc_rx(&name).recv_timeout(TIMEOUT) { Ok(msg) => unreachable!("should not receive: {:?}", msg), Err(err) => match err { RecvTimeoutError::Timeout => {} _ => unreachable!("should have timed out"), }, } } #[test] fn can_routing_be_based_on_message_id() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); let names = [Name::english("one"), Name::english("two")]; let mut inputs: HashMap = HashMap::new(); for name in names.iter() { tester.add_document(name.clone()); let input = Message::new(name.clone(), Query::new()); let path = Path::new( Include::Some(input.get_message_id().clone()), Include::All, Include::All, ); let reg_msg = RegMsg::AddRoute(path); let reg = Register::new(tester.get_doc_rx_id(&name).clone(), reg_msg); let msg = Message::new(NameType::None, reg); queue.send(msg).unwrap(); let result = tester.get_doc_rx(&name).recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Register(data) => match data.get_msg() { RegMsg::RouteID(output) => { let expected = RouteID { action: None, doc_type: None, msg_id: Some(input.get_message_id().clone()), }; assert_eq!(output, &expected); } _ => unreachable!("got {:?}, should have been route id", data), }, _ => unreachable!("got {:?}, should have been route id", action), } inputs.insert(name.clone(), input); } for msg in inputs.values() { queue.send(msg.clone()).unwrap(); } for (name, msg) in inputs.iter() { let rx = tester.get_doc_rx(&name); let result = rx.recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); match rx.recv_timeout(TIMEOUT) { Ok(msg) => unreachable!("should not receive: {:?}", msg), Err(err) => match err { RecvTimeoutError::Timeout => {} _ => unreachable!("should have timed out"), }, } } } #[test] fn can_routing_be_based_on_document_name() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); let names = [Name::english("one"), Name::english("two")]; let mut inputs: HashMap = HashMap::new(); for name in names.iter() { tester.add_document(name.clone()); let input = Message::new(name.clone(), Query::new()); let path = Path::new( Include::All, Include::Some(name.clone().into()), Include::All, ); let reg_msg = RegMsg::AddRoute(path); let reg = Register::new(tester.get_doc_rx_id(&name).clone(), reg_msg); let msg = Message::new(NameType::None, reg); queue.send(msg).unwrap(); let result = tester.get_doc_rx(&name).recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Register(data) => match data.get_msg() { RegMsg::RouteID(output) => { let expected = RouteID { action: None, doc_type: Some(tester.get_doc_id(name).clone()), msg_id: None, }; assert_eq!(output, &expected); } _ => unreachable!("got {:?}, should have been route id", data), }, _ => unreachable!("got {:?}, should have been route id", action), } inputs.insert(name.clone(), input); } for msg in inputs.values() { queue.send(msg.clone()).unwrap(); } for (name, msg) in inputs.iter() { let rx = tester.get_doc_rx(&name); let result = rx.recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); match rx.recv_timeout(TIMEOUT) { Ok(msg) => unreachable!("should not receive: {:?}", msg), Err(err) => match err { RecvTimeoutError::Timeout => {} _ => unreachable!("should have timed out"), }, } } } #[test] fn can_routing_be_based_on_action() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); let names = [Name::english("one"), Name::english("two")]; let paths = [ Path::new(Include::All, Include::All, Include::Some(Action::Reply)), Path::new(Include::All, Include::All, Include::Some(Action::Error)), ]; let actions = [ MsgAction::Reply(Reply::new()), MsgAction::Error(MTTError::NameDuplicate(names[0].clone())), ]; let mut inputs: HashMap = HashMap::new(); let mut count = 0; for name in names.iter() { tester.add_document(name.clone()); let input = Message::new(NameType::None, actions[count].clone()); let path = paths[count].clone(); let reg_msg = RegMsg::AddRoute(path); let reg = Register::new(tester.get_doc_rx_id(&name).clone(), reg_msg); let msg = Message::new(NameType::None, reg); queue.send(msg).unwrap(); let result = tester.get_doc_rx(&name).recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Register(data) => match data.get_msg() { RegMsg::RouteID(output) => { let expected = RouteID { action: Some(actions[count].clone().into()), doc_type: None, msg_id: None, }; assert_eq!(output, &expected); } _ => unreachable!("got {:?}, should have been route id", data), }, _ => unreachable!("got {:?}, should have been route id", action), } inputs.insert(name.clone(), input); count += 1; } for msg in inputs.values() { queue.send(msg.clone()).unwrap(); } for (name, msg) in inputs.iter() { let rx = tester.get_doc_rx(&name); let result = rx.recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); match rx.recv_timeout(TIMEOUT) { Ok(msg) => unreachable!("should not receive: {:?}", msg), Err(err) => match err { RecvTimeoutError::Timeout => {} _ => unreachable!("should have timed out"), }, } } } } struct CreateDoc { queue: Queue, rx: Receiver, } impl CreateDoc { fn new(queue: Queue, rx: Receiver) -> Self { Self { queue: queue, rx: rx, } } fn start(mut queue: Queue) { let (tx, rx) = channel(); let routes = [Path::new( Include::All, Include::All, Include::Some(Action::Create), )] .to_vec(); let id = queue.add_sender(tx); for route in routes.iter() { let regmsg = Register::new(id.clone(), RegMsg::AddRoute(route.clone())); queue.send(Message::new(NameType::None, regmsg)); rx.recv().unwrap(); } let doc = CreateDoc::new(queue, rx); spawn(move || { doc.listen(); }); } fn listen(&self) { loop { let msg = self.rx.recv().unwrap(); DocumentFile::start(self.queue.clone(), msg); } } } #[derive(Clone, Debug, PartialEq)] enum FieldType { Boolean, DateTime, Duration, Integer, None, StaticString, Uuid, } impl FieldType { fn get_default(&self) -> Field { match self { FieldType::Boolean => false.into(), FieldType::DateTime => Utc::now().into(), FieldType::Duration => Duration::from_secs(0).into(), FieldType::Integer => 0.into(), FieldType::None => Field::None, FieldType::StaticString => "".into(), FieldType::Uuid => Uuid::new_v4().into(), } } } impl From<&Field> for FieldType { fn from(value: &Field) -> Self { match value { Field::Boolean(_) => Self::Boolean, Field::DateTime(_) => Self::DateTime, Field::Duration(_) => Self::Duration, Field::Integer(_) => Self::Integer, Field::None => Self::None, Field::StaticString(_) => Self::StaticString, Field::Uuid(_) => Self::Uuid, } } } #[cfg(test)] mod fieldtypes { use super::*; #[test] fn can_get_defaults_for_uuid() { let ftype = FieldType::Uuid; let mut ids: Vec = Vec::new(); for _ in 0..5 { let result = ftype.get_default(); match result { Field::Uuid(data) => { assert!( !ids.contains(&data), "found duplicate id {:?} in {:?}", data, ids ); ids.push(data.clone()); } _ => unreachable!("got {:?}: should have been uuid", result), } } } #[test] fn can_get_defaults_for_static_string() { let ftype = FieldType::StaticString; let result = ftype.get_default(); match result { Field::StaticString(data) => assert_eq!(data, ""), _ => unreachable!("got {:?}: should have been static string", result), } } } #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] enum Field { Boolean(bool), DateTime(DateTime), Duration(Duration), Integer(i128), None, StaticString(String), Uuid(Uuid), } impl Field { fn get_type(&self) -> FieldType { self.into() } fn equal(&self, other: &Field) -> Field { if self.get_type() == other.get_type() { { self == other }.into() } else { Field::None } } fn not_equal(&self, other: &Field) -> Field { if self.get_type() == other.get_type() { { self != other }.into() } else { Field::None } } fn greater(&self, other: &Self) -> Field { if self.get_type() == other.get_type() { { self > other }.into() } else { Field::None } } fn greater_equal(&self, other: &Self) -> Field { if self.get_type() == other.get_type() { { self >= other }.into() } else { Field::None } } fn lesser(&self, other: &Self) -> Field { if self.get_type() == other.get_type() { { self < other }.into() } else { Field::None } } fn lesser_equal(&self, other: &Self) -> Field { if self.get_type() == other.get_type() { { self <= other }.into() } else { Field::None } } } impl Add for Field { type Output = Self; fn add(self, other: Self) -> Self { match self { Field::DateTime(value1) => match other { Field::Duration(value2) => { value1 + value2 }.into(), _ => Field::None, }, Field::Duration(value1) => match other { Field::Duration(value2) => { value1 + value2 }.into(), _ => Field::None, }, Field::Integer(value1) => match other { Field::Integer(value2) => { value1 + value2 }.into(), _ => Field::None, }, _ => Self::None, } } } impl AddAssign for Field { fn add_assign(&mut self, other: Self) { *self = self.clone().add(other); } } impl From for Field { fn from(value: bool) -> Self { Self::Boolean(value) } } impl From> for Field { fn from(value: DateTime) -> Self { Self::DateTime(value) } } impl From for Field { fn from(value: Duration) -> Self { Self::Duration(value) } } impl From for Field { fn from(value: String) -> Self { Self::StaticString(value) } } impl From<&str> for Field { fn from(value: &str) -> Self { Self::from(value.to_string()) } } impl From for Field { fn from(value: Uuid) -> Self { Self::Uuid(value) } } impl From for Field { fn from(value: i128) -> Self { Self::Integer(value) } } impl From for Field { fn from(value: isize) -> Self { let data: i128 = value.try_into().unwrap(); Self::from(data) } } impl From for Field { fn from(value: i32) -> Self { let data: i128 = value.into(); Self::from(data) } } #[cfg(test)] mod fields { use super::*; use rand::random; #[test] fn can_create_static_string() { let data = Uuid::new_v4().to_string(); let result: Field = data.clone().into(); match result.clone() { Field::StaticString(output) => assert_eq!(output, data), _ => unreachable!("got {:?}: should have been static string", result), } assert_eq!(result.get_type(), FieldType::StaticString); } #[test] fn can_create_from_str() { let holder = ["one", "two"]; for data in holder.into_iter() { let result: Field = data.into(); match result.clone() { Field::StaticString(output) => assert_eq!(output, data), _ => unreachable!("got {:?}: should have been static string", result), } assert_eq!(result.get_type(), FieldType::StaticString); } } #[test] fn create_from_uuid() { let data = Uuid::new_v4(); let result: Field = data.clone().into(); match result.clone() { Field::Uuid(output) => assert_eq!(output, data), _ => unreachable!("got {:?}: should have been uuid", result), } assert_eq!(result.get_type(), FieldType::Uuid); } #[test] fn create_from_datatime() { let data = Utc::now(); let result: Field = data.into(); match result.clone() { Field::DateTime(output) => assert_eq!(output, data), _ => unreachable!("got {:?}: should have been uuid", result), } assert_eq!(result.get_type(), FieldType::DateTime); } #[test] fn does_adding_return_none_for_things_that_cannot_add() { let value1: Field = Uuid::new_v4().into(); let value2: Field = Uuid::new_v4().into(); assert_eq!(value1 + value2, Field::None); } #[test] fn can_integers_be_added() { let value1: i128 = random::().into(); let value2: i128 = random::().into(); let expected: Field = { value1 + value2 }.into(); let value1: Field = value1.into(); let value2: Field = value2.into(); assert_eq!(value1 + value2, expected); } #[test] fn can_integer_add_mismatch_returns_none() { let value1: Field = 5.into(); let value2: Field = "nope".into(); assert_eq!(value1 + value2, Field::None); } #[test] fn can_durations_be_added() { let data1: u64 = random::().into(); let data2: u64 = random::().into(); let value1: Field = Duration::from_secs(data1).into(); let value2: Field = Duration::from_secs(data2).into(); let expected: Field = Duration::from_secs(data1 + data2).into(); assert_eq!(value1 + value2, expected); } #[test] fn does_duration_mismatch_return_none() { let value1: Field = Duration::from_secs(20).into(); let value2: Field = "nope".into(); assert_eq!(value1 + value2, Field::None); } #[test] fn can_durations_be_added_to_datetimes() { let timestamp = Utc::now(); let data: u64 = random::().into(); let duration = Duration::from_secs(data); let expected: Field = { timestamp + duration }.into(); let value1: Field = timestamp.into(); let value2: Field = duration.into(); assert_eq!(value1 + value2, expected); } #[test] fn does_datetime_mismatch_return_none() { let value1: Field = Utc::now().into(); let value2: Field = "nope".into(); assert_eq!(value1 + value2, Field::None); } #[test] fn does_field_equal_return_properly() { let mut values: Vec = Vec::new(); let count = 2; while values.len() < count { let value: Field = Uuid::new_v4().into(); if !values.contains(&value) { values.push(value); } } assert_eq!(values[0].equal(&values[0]), true.into()); assert_eq!(values[0].equal(&values[1]), false.into()); assert_eq!(values[0].equal(&"nope".into()), Field::None); } #[test] fn does_field_not_equal_return_properly() { let mut values: Vec = Vec::new(); let count = 2; while values.len() < count { let value: Field = Uuid::new_v4().into(); if !values.contains(&value) { values.push(value); } } assert_eq!(values[0].not_equal(&values[0]), false.into()); assert_eq!(values[0].not_equal(&values[1]), true.into()); assert_eq!(values[0].not_equal(&"nope".into()), Field::None); } #[test] fn can_field_have_greater_value() { let value1: Field = 1.into(); let value2: Field = 2.into(); let value3: Field = 3.into(); let mismatch: Field = "bad".into(); assert_eq!(value2.greater(&value1), true.into()); assert_eq!(value2.greater(&value2), false.into()); assert_eq!(value2.greater(&value3), false.into()); assert_eq!(value2.greater(&mismatch), Field::None); } #[test] fn can_field_have_greater_or_equal_value() { let value1: Field = 1.into(); let value2: Field = 2.into(); let value3: Field = 3.into(); let mismatch: Field = "bad".into(); assert_eq!(value2.greater_equal(&value1), true.into()); assert_eq!(value2.greater_equal(&value2), true.into()); assert_eq!(value2.greater_equal(&value3), false.into()); assert_eq!(value2.greater_equal(&mismatch), Field::None); } #[test] fn can_field_have_lesser_value() { let value1: Field = 1.into(); let value2: Field = 2.into(); let value3: Field = 3.into(); let mismatch: Field = "bad".into(); assert_eq!(value2.lesser(&value1), false.into()); assert_eq!(value2.lesser(&value2), false.into()); assert_eq!(value2.lesser(&value3), true.into()); assert_eq!(value2.lesser(&mismatch), Field::None); } #[test] fn can_field_have_lesser_or_equal_value() { let value1: Field = 1.into(); let value2: Field = 2.into(); let value3: Field = 3.into(); let mismatch: Field = "bad".into(); assert_eq!(value2.lesser_equal(&value1), false.into()); assert_eq!(value2.lesser_equal(&value2), true.into()); assert_eq!(value2.lesser_equal(&value3), true.into()); assert_eq!(value2.lesser_equal(&mismatch), Field::None); } } #[derive(Clone, Debug)] struct FieldSetting { fieldtype: FieldType, default_value: CalcValue, } impl FieldSetting { fn new(ftype: FieldType) -> Self { Self { fieldtype: ftype, default_value: CalcValue::None, } } fn set_default(&mut self, holder: CV) -> Result<(), MTTError> where CV: Into, { let value = holder.into(); match &value { CalcValue::Calculate(calc) => { if self.fieldtype != calc.get_type() { return Err(MTTError::FieldInvalidType); } } CalcValue::Existing(ftype) | CalcValue::FType(ftype) => { if &self.fieldtype != ftype { return Err(MTTError::FieldInvalidType); } } CalcValue::Value(ref item) => { if item.get_type() != self.fieldtype { return Err(MTTError::FieldInvalidType); } } CalcValue::None => {} } self.default_value = value; Ok(()) } fn validate(&self, value: &Field) -> Result { match value { Field::None => match &self.default_value { CalcValue::None => Err(MTTError::InvalidNone), _ => Ok(self.default_value.get(&Field::None)), }, _ => { let vft: FieldType = value.into(); if vft != self.fieldtype { return Err(MTTError::DocumentFieldWrongDataType( self.fieldtype.clone(), vft, )); } Ok(value.clone()) } } } } #[cfg(test)] mod fieldsettings { use super::*; #[test] fn validates_field_type() { let fset = FieldSetting::new(FieldType::Uuid); let value: Field = Uuid::new_v4().into(); match fset.validate(&value) { Ok(data) => assert_eq!(data, value), Err(err) => unreachable!("got {:?}: should have gotten a value", err), } } #[test] fn validates_for_bad_field_type() { let fset = FieldSetting::new(FieldType::Uuid); let value: Field = "text".into(); match fset.validate(&value) { Ok(data) => unreachable!("got {:?}: should have gotten an error", data), Err(err) => match err { MTTError::DocumentFieldWrongDataType(expected, got) => { assert_eq!(expected, FieldType::Uuid); assert_eq!(got, FieldType::StaticString); } _ => unreachable!("got {:?}: should have gotten a value", err), }, } } #[test] fn no_default_returns_error() { let fset = FieldSetting::new(FieldType::Uuid); match fset.validate(&Field::None) { Ok(data) => unreachable!("got {:?}: should have gotten an error", data), Err(err) => match err { MTTError::InvalidNone => {} _ => unreachable!("got {:?}: should have gotten a invalid none", err), }, } } #[test] fn returns_value_if_default_is_set() { let mut fset = FieldSetting::new(FieldType::StaticString); fset.set_default(FieldType::StaticString); match fset.validate(&Field::None) { Ok(data) => assert_eq!(data, "".into()), Err(err) => unreachable!("got {:?}: should have gotten a value", err), } } #[test] fn returns_default_value() { let mut fset = FieldSetting::new(FieldType::StaticString); let input = "fred"; fset.set_default(input); match fset.validate(&Field::None) { Ok(data) => assert_eq!(data, input.into()), Err(err) => unreachable!("got {:?}: should have gotten a value", err), } } #[test] fn can_default_be_calculated() { let mut fset = FieldSetting::new(FieldType::DateTime); let duration = Duration::from_secs(3600); let mut calc = Calculation::new(Operand::Add); calc.add_value(FieldType::DateTime); calc.add_value(duration); fset.set_default(calc); let start = Utc::now() + duration; let result = match fset.validate(&Field::None).unwrap() { Field::DateTime(data) => data, _ => unreachable!("should return datetime"), }; let stop = Utc::now() + duration; assert!( result > start, "{:?} should have been greater than {:?}", result, start ); assert!( result < stop, "{:?} should have been less than {:?}", result, stop ); } } #[derive(Clone, Debug)] struct Addition { data: Document, } impl Addition { fn new() -> Self { Self { data: Document::new(), } } fn add_field(&mut self, name: NT, field: CV) where CV: Into, NT: Into, { self.data.add_field(name, field); } fn get_field(&self, name: NT) -> &CalcValue where NT: Into, { self.data.get_field(name) } fn get_document(&self) -> Document { self.data.clone() } fn iter(&self) -> impl Iterator { self.data.iter() } } #[cfg(test)] mod additions { use super::*; #[test] fn can_add_static_string() { let mut add = Addition::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4().to_string(); add.add_field(name.clone(), data.clone()); let result = add.get_field(&name); match result { CalcValue::Value(result) => match result { Field::StaticString(output) => assert_eq!(output, &data), _ => unreachable!("got {:?}, should have been a string", result), }, _ => unreachable!("got {:?}: should have received value", result), } } #[test] fn can_add_uuid() { let mut add = Addition::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4(); add.add_field(name.clone(), data.clone()); let output = add.get_field(&name); match output { CalcValue::Value(result) => match result { Field::Uuid(result) => assert_eq!(result, &data), _ => unreachable!("got {:?}: should have received uuid", result), }, _ => unreachable!("got {:?}: should have received value", output), } } #[test] fn can_get_document() { let mut add = Addition::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4(); add.add_field(name.clone(), data.clone()); let doc = add.get_document(); let output = doc.get_field(&name); match output { CalcValue::Value(holder) => match holder { Field::Uuid(result) => assert_eq!(result, &data), _ => unreachable!("should have received uuid"), }, _ => unreachable!("got {:?}: should have received value", output), } } } #[derive(Clone, Debug)] enum IndexType { Index, Unique, } impl IndexType { fn create_index(&self) -> Index { match self { Self::Index => Index::new(), Self::Unique => Index::new_unique(), } } } #[derive(Clone, Debug)] enum DocFuncType { Add, Delete, ExistingQuery(MsgAction), Query, Show, Trigger(MsgAction), Update, } #[derive(Clone, Debug)] struct PathAction { path: Path, func_type: DocFuncType, } impl PathAction { fn new(path: Path, func_type: DocFuncType) -> Self { Self { path: path, func_type: func_type, } } fn path(&self) -> Path { self.path.clone() } fn doc_function(&self) -> DocFuncType { self.func_type.clone() } } #[derive(Clone, Debug)] struct DocDef { doc_names: Vec, field_names: Names, fields: HashMap, indexes: HashMap, routes: Vec, } impl DocDef { fn new(name: Name) -> Self { let names = vec![name]; Self::with_names(names) } fn with_names(names: Vec) -> Self { let routes = vec![ PathAction::new( Path::new( Include::All, Include::Some(names[0].clone().into()), Include::Some(Action::Addition), ), DocFuncType::Add, ), PathAction::new( Path::new( Include::All, Include::Some(names[0].clone().into()), Include::Some(Action::Delete), ), DocFuncType::Delete, ), PathAction::new( Path::new( Include::All, Include::Some(names[0].clone().into()), Include::Some(Action::Query), ), DocFuncType::Query, ), PathAction::new( Path::new( Include::All, Include::Some(names[0].clone().into()), Include::Some(Action::Show), ), DocFuncType::Show, ), PathAction::new( Path::new( Include::All, Include::Some(names[0].clone().into()), Include::Some(Action::Update), ), DocFuncType::Update, ), ]; Self { doc_names: names, field_names: Names::new(), fields: HashMap::new(), indexes: HashMap::new(), routes: routes, } } fn get_document_names(&self) -> &Vec { &self.doc_names } fn get_field_names(&self) -> &Names { &self.field_names } fn get_field_names_mut(&mut self) -> &mut Names { &mut self.field_names } fn add_field(&mut self, name: Name, ftype: FieldType) { let id = self.field_names.add_names([name].to_vec()).unwrap(); self.fields.insert(id, FieldSetting::new(ftype)); } fn get_field_id(&self, field_name: NT) -> Result where NT: Into, { match self.field_names.get_id(field_name) { Ok(data) => Ok(data), Err(err) => Err(err), } } fn get_field(&self, field_name: NT) -> Result<&FieldSetting, MTTError> where NT: Into, { let id = match self.field_names.get_id(field_name) { Ok(data) => data, Err(err) => return Err(err), }; Ok(self.fields.get(&id).unwrap()) } fn get_field_mut(&mut self, field_name: NT) -> Result<&mut FieldSetting, MTTError> where NT: Into, { let id = match self.field_names.get_id(field_name) { Ok(data) => data, Err(err) => return Err(err), }; Ok(self.fields.get_mut(&id).unwrap()) } fn field_ids(&self) -> HashSet { self.fields.keys().cloned().collect() } fn validate(&self, field_name: NT, value: &Field) -> Result where NT: Into, { let id = match self.field_names.get_id(field_name) { Ok(data) => data, Err(err) => return Err(err), }; self.fields.get(&id).unwrap().validate(value) } fn set_default(&mut self, field_name: &Name, value: CV) -> Result<(), MTTError> where CV: Into, { let id = match self.field_names.get_id(field_name) { Ok(data) => data, Err(err) => return Err(err), }; match self.fields.get_mut(&id).unwrap().set_default(value) { Ok(_) => Ok(()), Err(err) => Err(err), } } fn add_index(&mut self, field_name: &Name, index_type: IndexType) -> Result<(), MTTError> { let id = match self.field_names.get_id(field_name) { Ok(data) => data, Err(err) => return Err(err), }; self.indexes.insert(id.clone(), index_type); Ok(()) } fn create_indexes(&self) -> Indexes { Indexes::new(&self.indexes) } fn iter(&self) -> impl Iterator { self.fields.iter() } fn iter_routes(&self) -> impl Iterator { self.routes.iter() } fn add_route(&mut self, path: Path, action: DocFuncType) { self.routes.push(PathAction::new(path, action)); } } #[cfg(test)] mod docdefs { use super::*; #[test] fn can_field_be_added() { let docname = Name::english("tester"); let mut docdef = DocDef::new(docname); let name = Name::english(Uuid::new_v4().to_string().as_str()); let field_type = FieldType::Uuid; docdef.add_field(name.clone(), field_type.clone()); let result = docdef.get_field(name).unwrap(); match result.validate(&Uuid::new_v4().into()) { Ok(_) => {} Err(err) => unreachable!("got {:?}: should have been a value", err), } } #[test] fn produces_error_for_bad_fields() { let docname = Name::english("tester"); let docdef = DocDef::new(docname); let name = Name::english(Uuid::new_v4().to_string().as_str()); match docdef.get_field(&name) { Ok(_) => unreachable!("should return non existant field error"), Err(err) => match err { MTTError::NameNotFound(data) => assert_eq!(data, name), _ => unreachable!("got {:?}: should have been document field not found", err), }, } } #[test] fn can_multiple_fields_be_added() { let docname = Name::english("testing"); let mut docdef = DocDef::new(docname); let names = ["one", "two", "three"]; let field_type = FieldType::StaticString; for name in names.iter() { docdef.add_field(Name::english(name.clone()), field_type.clone()); } for name in names.iter() { let result = docdef.get_field(Name::english(name.clone())).unwrap(); match result.validate(&"".into()) { Ok(_) => {} Err(err) => unreachable!("got {:?}: should have been a value", err), } } } #[test] fn can_change_field_default_to_function() { let docname = Name::english("something"); let mut docdef = DocDef::new(docname); let name = Name::english("defaultfunction"); docdef.add_field(name.clone(), FieldType::StaticString); docdef.set_default(&name, FieldType::StaticString); match docdef.get_field(name).unwrap().validate(&Field::None) { Ok(data) => match data { Field::StaticString(result) => assert_eq!(result, ""), _ => unreachable!("got {:?}: should return a static string", data), }, Err(err) => unreachable!("got {:?}: should return a value", err), } } #[test] fn does_set_default_value_error_on_bad_field_name() { let docname = Name::english("something"); let mut docdef = DocDef::new(docname); let field_name = Name::english(Uuid::new_v4().to_string().as_str()); match docdef.set_default(&field_name, FieldType::Uuid) { Ok(_) => unreachable!("should be an error"), Err(err) => match err { MTTError::NameNotFound(data) => assert_eq!(data, field_name), _ => unreachable!("got {:?}: should have been field not found", err), }, } } #[test] fn does_set_default_value_error_on_bad_field_type() { let docname = Name::english("something"); let mut docdef = DocDef::new(docname); let name = Name::english("defaultvalue"); docdef.add_field(name.clone(), FieldType::Uuid); match docdef.set_default(&name, "fred") { Ok(data) => unreachable!("got {:?}, should be an error", data), Err(err) => match err { MTTError::FieldInvalidType => {} _ => unreachable!("got {:?}: should have been field not found", err), }, } } #[test] fn does_default_routes_get_set() { let default_num = 5; let docname = Name::english(Uuid::new_v4().to_string().as_str()); let docdef = DocDef::new(docname.clone()); assert_eq!( docdef.iter_routes().count(), default_num, "routes contained the following:\n{:?}", docdef.routes ); let mut actions: HashSet = HashSet::new(); let mut doc_funcs: HashSet = HashSet::new(); for path_action in docdef.iter_routes() { let path = path_action.path(); match &path.msg_id { Include::All => {} _ => unreachable!("got {:?}, message id should include all", path.msg_id), } match &path.doc { Include::Some(output) => match output { NameType::Name(data) => assert_eq!(data, &docname), _ => unreachable!("got {:?}, name type should be {:?}", path.doc, docname), }, _ => unreachable!("got {:?}, name type should be {:?}", path.doc, docname), }; match &path.action { Include::Some(output) => match output { Action::Addition => actions.insert(output.clone()), Action::Delete => actions.insert(output.clone()), Action::Query => actions.insert(output.clone()), Action::Show => actions.insert(output.clone()), Action::Update => actions.insert(output.clone()), _ => unreachable!("got {:?} which is not a default action", output), }, _ => unreachable!("got {:?}, which is not a default action", path.action), }; let file_func = path_action.doc_function(); match file_func { DocFuncType::Add => doc_funcs.insert("Add".to_string()), DocFuncType::Delete => doc_funcs.insert("Delete".to_string()), DocFuncType::Query => doc_funcs.insert("Query".to_string()), DocFuncType::Show => doc_funcs.insert("Show".to_string()), DocFuncType::Update => doc_funcs.insert("Update".to_string()), _ => unreachable!("got {:?}, which is not a default function", file_func), }; } assert_eq!( actions.len(), default_num, "got {:?}, missing some actions", actions ); assert_eq!( doc_funcs.len(), default_num, "got {:?}, missing some actions", doc_funcs ); } #[test] fn add_route_function() { let docname = Name::english(Uuid::new_v4().to_string().as_str()); let mut docdef = DocDef::new(docname.clone()); docdef.add_route( Path::new( Include::All, Include::Some(docname.clone().into()), Include::Some(Action::OnQuery), ), DocFuncType::Trigger(Update::new(Query::new()).into()), ); let path_action = docdef.iter_routes().last().unwrap(); let path = path_action.path(); match &path.msg_id { Include::All => {} _ => unreachable!("got {:?}, message id should include all", path.msg_id), }; match &path.doc { Include::Some(output) => match output { NameType::Name(data) => assert_eq!(data, &docname), _ => unreachable!("got {:?}, name type should be {:?}", path.doc, docname), }, _ => unreachable!("got {:?}, name type should be {:?}", path.doc, docname), }; match &path.action { Include::Some(output) => match output { Action::OnQuery => {} _ => unreachable!("got {:?} which is not a additional action", output), }, _ => unreachable!("got {:?}, which is not on query action", path.action), } let file_func = path_action.doc_function(); match file_func { DocFuncType::Trigger(_) => {} _ => unreachable!("got {:?}, which is not a default function", file_func), } } } #[derive(Clone, Debug)] enum Operand { Add, Equal, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual, } impl Operand { fn validate(&self, x: &Field, y: &Field) -> bool { match self { Self::Equal => x == y, Self::GreaterThan => x > y, Self::GreaterThanEqual => x >= y, Self::LessThan => x < y, Self::LessThanEqual => x <= y, _ => false, } } } #[cfg(test)] mod operands { use super::*; #[test] fn equals_true() { let data: Field = Uuid::new_v4().into(); assert!(Operand::Equal.validate(&data, &data)); } #[test] fn equals_false() { let x: Field = Uuid::new_v4().into(); let mut y: Field = Uuid::new_v4().into(); while x == y { y = Uuid::new_v4().into(); } assert!(!Operand::Equal.validate(&x, &y)); } #[test] fn does_greater() { let data: Vec = vec![1.into(), 2.into(), 3.into()]; assert!(!Operand::GreaterThan.validate(&data[0], &data[1])); assert!(!Operand::GreaterThan.validate(&data[1], &data[1])); assert!(Operand::GreaterThan.validate(&data[2], &data[1])); } #[test] fn does_greater_equal() { let data: Vec = vec![1.into(), 2.into(), 3.into()]; assert!(!Operand::GreaterThanEqual.validate(&data[0], &data[1])); assert!(Operand::GreaterThanEqual.validate(&data[1], &data[1])); assert!(Operand::GreaterThanEqual.validate(&data[2], &data[1])); } #[test] fn does_lesser() { let data: Vec = vec![1.into(), 2.into(), 3.into()]; assert!(Operand::LessThan.validate(&data[0], &data[1])); assert!(!Operand::LessThan.validate(&data[1], &data[1])); assert!(!Operand::LessThan.validate(&data[2], &data[1])); } #[test] fn does_lesser_equal() { let data: Vec = vec![1.into(), 2.into(), 3.into()]; assert!(Operand::LessThanEqual.validate(&data[0], &data[1])); assert!(Operand::LessThanEqual.validate(&data[1], &data[1])); assert!(!Operand::LessThanEqual.validate(&data[2], &data[1])); } } #[derive(Clone, Debug)] enum CalcValue { Calculate(Calculation), Existing(FieldType), FType(FieldType), None, Value(Field), } impl CalcValue { fn get(&self, existing: &Field) -> Field { match self { Self::Calculate(calc) => calc.calculate(existing), Self::Existing(_) => existing.clone(), Self::FType(ftype) => ftype.get_default(), Self::None => Field::None, Self::Value(field) => field.clone(), } } fn get_type(&self) -> FieldType { match self { Self::Calculate(calc) => calc.get_type(), Self::Existing(ftype) => ftype.clone(), Self::FType(ftype) => ftype.clone(), Self::None => FieldType::None, Self::Value(field) => field.into(), } } } impl From for CalcValue { fn from(value: Calculation) -> Self { Self::Calculate(value) } } impl From for CalcValue { fn from(value: Field) -> Self { Self::Value(value) } } impl From<&Field> for CalcValue { fn from(value: &Field) -> Self { Self::from(value.clone()) } } impl From for CalcValue { fn from(value: FieldType) -> Self { Self::FType(value) } } impl From for CalcValue { fn from(value: bool) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From> for CalcValue { fn from(value: DateTime) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From for CalcValue { fn from(value: Duration) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From for CalcValue { fn from(value: i128) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From<&str> for CalcValue { fn from(value: &str) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From for CalcValue { fn from(value: String) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From for CalcValue { fn from(value: Uuid) -> Self { let output: Field = value.into(); Self::from(output).into() } } #[cfg(test)] mod calcvalues { use super::*; #[test] fn from_uuid() { let value = Uuid::new_v4(); let expected: Field = value.into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_str() { let value = "something"; let expected: Field = value.into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_string() { let value = "data".to_string(); let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_boolean() { let value = true; let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_datetime() { let value = Utc::now(); let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_duration() { let value = Duration::from_secs(5); let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_integer() { let value: i128 = 5; let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_calculation() { let duration = Duration::from_secs(300); let start = Utc::now() + duration; let mut calc = Calculation::new(Operand::Add); calc.add_value(FieldType::DateTime); calc.add_value(duration.clone()); let result: CalcValue = calc.into(); let data = match result.get(&Field::None) { Field::DateTime(data) => data, _ => unreachable!(), }; let stop = Utc::now() + duration; assert!( data > start && data < stop, "should be about 5 minutes ahead" ); } } #[derive(Clone, Debug)] struct Calculation { operation: Operand, values: Vec, } impl Calculation { fn new(operand: Operand) -> Self { Self { operation: operand, values: Vec::new(), } } fn operation(&self) -> &Operand { &self.operation } fn get_fields(&self, existing: Field) -> Vec { let mut output = Vec::new(); for item in self.values.iter() { output.push(item.get(&existing)); } output } fn get_type(&self) -> FieldType { if self.values.is_empty() { FieldType::None } else { self.values[0].get_type() } } fn add_value(&mut self, data: CV) -> Result<(), MTTError> where CV: Into, { let holder: CalcValue = data.into(); if self.values.is_empty() { self.values.push(holder); return Ok(()); } let mut base = self.get_type(); match self.operation { Operand::Add => match base { FieldType::DateTime => base = FieldType::Duration, _ => {} }, _ => {} } let ftype = holder.get_type(); if base == ftype { self.values.push(holder); } else { return Err(MTTError::DocumentFieldWrongDataType(base, ftype)); } Ok(()) } fn validate_value(&self, value: CV) -> Result<(), MTTError> where CV: Into, { if self.values.is_empty() { return Ok(()); } let holder = value.into(); let mut base = self.get_type(); match self.operation { Operand::Add => { if base == FieldType::DateTime { base = FieldType::Duration; } } _ => {} } let ftype = holder.get_type(); if base == ftype { Ok(()) } else { Err(MTTError::DocumentFieldWrongDataType(base, ftype)) } } fn calculate(&self, existing: &Field) -> Field { let mut result = Field::None; match self.operation { Operand::Add => { let mut first = true; for value in self.values.iter() { let data = value.get(existing); if first { result = data; first = false; } else { result += data; } } } Operand::Equal => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .equal(&self.values[1].get(existing)); } } Operand::GreaterThan => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .greater(&self.values[1].get(existing)); } } Operand::GreaterThanEqual => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .greater_equal(&self.values[1].get(existing)); } } Operand::LessThan => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .lesser(&self.values[1].get(existing)); } } Operand::LessThanEqual => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .lesser_equal(&self.values[1].get(existing)); } } } result } } #[cfg(test)] mod calculations { use super::*; use rand::random; #[test] fn errors_on_different_field_types() { let mut calc = Calculation::new(Operand::Equal); calc.add_value(Uuid::nil()).unwrap(); match calc.add_value("other") { Ok(_) => unreachable!("should have errored with wrong type"), Err(err) => match err { MTTError::DocumentFieldWrongDataType(expected, got) => { assert_eq!(expected, FieldType::Uuid); assert_eq!(got, FieldType::StaticString); } _ => unreachable!("got {:?}, expected wrong field type", err), }, } } #[test] fn returns_reference_to_operand() { let calc = Calculation::new(Operand::Add); match calc.operation() { Operand::Add => {} _ => unreachable!("got {:?}, shold have gotten assign", calc.operation()), } let calc = Calculation::new(Operand::Equal); match calc.operation() { Operand::Equal => {} _ => unreachable!("got {:?}, shold have gotten assign", calc.operation()), } } #[test] fn can_equal_true() { let mut calc = Calculation::new(Operand::Equal); let data: Field = Uuid::new_v4().into(); calc.add_value(data.clone()); calc.add_value(data.clone()); let expected: Field = true.into(); let result = calc.calculate(&Field::None); assert_eq!(result, expected); } #[test] fn can_equal_false() { let mut calc = Calculation::new(Operand::Equal); let value1: Field = "fred".into(); let value2: Field = "barney".into(); calc.add_value(value1); calc.add_value(value2); let expected: Field = false.into(); let result = calc.calculate(&Field::None); assert_eq!(result, expected); } #[test] fn can_greater_than() { let data: Vec<(Field, Field)> = vec![ (0.into(), false.into()), (1.into(), false.into()), (2.into(), true.into()), ]; for (item, expected) in data.iter() { let mut calc = Calculation::new(Operand::GreaterThan); calc.add_value(item.clone()); calc.add_value(data[1].0.clone()); let result = calc.calculate(&Field::None); assert_eq!(&result, expected); } } #[test] fn can_greater_than_equal() { let data: Vec<(Field, Field)> = vec![ (0.into(), false.into()), (1.into(), true.into()), (2.into(), true.into()), ]; for (item, expected) in data.iter() { let mut calc = Calculation::new(Operand::GreaterThanEqual); calc.add_value(item.clone()); calc.add_value(data[1].0.clone()); let result = calc.calculate(&Field::None); assert_eq!(&result, expected); } } #[test] fn can_lesser_than() { let data: Vec<(Field, Field)> = vec![ (0.into(), true.into()), (1.into(), false.into()), (2.into(), false.into()), ]; for (item, expected) in data.iter() { let mut calc = Calculation::new(Operand::LessThan); calc.add_value(item.clone()); calc.add_value(data[1].0.clone()); let result = calc.calculate(&Field::None); assert_eq!(&result, expected); } } #[test] fn can_lesser_than_equal() { let data: Vec<(Field, Field)> = vec![ (0.into(), true.into()), (1.into(), true.into()), (2.into(), false.into()), ]; for (item, expected) in data.iter() { let mut calc = Calculation::new(Operand::LessThanEqual); calc.add_value(item.clone()); calc.add_value(data[1].0.clone()); let result = calc.calculate(&Field::None); assert_eq!(&result, expected); } } #[test] fn can_add_numbers() { let mut calc = Calculation::new(Operand::Add); let value1: i128 = random::().into(); let value2: i128 = random::().into(); let expected: Field = { value1 + value2 }.into(); let value1: Field = value1.into(); let value2: Field = value2.into(); calc.add_value(value1.clone()); calc.add_value(value2.clone()); let result = calc.calculate(&Field::None); assert_eq!( result, expected, "{:?} plus {:?} should equal {:?}", value1, value2, expected ); } #[test] fn can_use_existing_values() { let mut calc = Calculation::new(Operand::Add); let value1: i128 = random::().into(); let value2: i128 = random::().into(); let expected: Field = { value1 + value2 }.into(); let value1: Field = value1.into(); let value2: Field = value2.into(); calc.add_value(value1.clone()); calc.add_value(CalcValue::Existing(FieldType::Integer)); let result = calc.calculate(&value2); assert_eq!( result, expected, "{:?} plus {:?} should equal {:?}", value1, value2, expected ); } #[test] fn returns_error_on_mismatch() { let mut calc = Calculation::new(Operand::Add); calc.add_value(Uuid::nil()); match calc.add_value("mismatch") { Ok(_) => unreachable!("should have returned an error"), Err(err) => match err { MTTError::DocumentFieldWrongDataType(expected, got) => { assert_eq!(got, FieldType::StaticString); assert_eq!(expected, FieldType::Uuid); } _ => unreachable!("got {:?}, expected wrong field type", err), }, } } #[test] fn datetime_accepts_duration() { let mut calc = Calculation::new(Operand::Add); let duration = Duration::from_secs(3600); let start = Utc::now() + duration; calc.add_value(FieldType::DateTime).unwrap(); match calc.add_value(duration.clone()) { Ok(_) => {} Err(err) => unreachable!("got {:?}, should have returned normally", err), } let result = calc.calculate(&Field::None); let stop = Utc::now() + duration; match result { Field::DateTime(data) => { assert!(data > start); assert!(data < stop); } _ => unreachable!("got {:?}, should have been datetime", result), } } } #[derive(Clone, Debug)] struct Operation { field_name: String, operation: Operand, value: Field, } impl Operation { fn new(name: String, op: Operand, value: F) -> Self where F: Into, { Self { field_name: name, operation: op, value: value.into(), } } fn which_field(&self) -> String { self.field_name.clone() } fn validate(&self, field: &Field) -> bool { self.operation.validate(field, &self.value) } } #[derive(Clone, Debug)] enum QueryType { Query(Query), Oids(HashSet), } impl From for QueryType { fn from(value: Query) -> Self { QueryType::Query(value) } } impl From> for QueryType { fn from(value: HashSet) -> Self { QueryType::Oids(value) } } #[derive(Clone, Debug)] struct Query { data: HashMap, } impl Query { fn new() -> Self { Self { data: HashMap::new(), } } fn add(&mut self, name: NT, operation: Calculation) -> Result<(), MTTError> where NT: Into, { match operation.operation() { Operand::Equal => { self.data.insert(name.into(), operation); Ok(()) } _ => Err(MTTError::QueryCannotChangeData), } } fn get(&self, name: NT) -> Option where NT: Into, { match self.data.get(&name.into()) { Some(calc) => Some(calc.clone()), None => None, } } fn field_ids(&self) -> HashSet<&NameType> { self.data.keys().collect() } fn iter(&self) -> impl Iterator { self.data.iter() } } #[cfg(test)] mod queries { use super::*; #[test] fn holds_calculation_to_run_query() { let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4(); let mut bad_data = data.clone(); while bad_data == data { bad_data = Uuid::new_v4(); } let mut query = Query::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value(data.clone()); query.add(name.clone(), calc); match query.get(&name) { Some(op) => { let expected: Field = true.into(); let mut holder = op.clone(); holder.add_value(data); assert_eq!(holder.calculate(&Field::None), expected); } None => unreachable!("should have returned a calculation"), } match query.get(&name) { Some(op) => { let expected: Field = false.into(); let mut holder = op.clone(); holder.add_value(bad_data); assert_eq!(holder.calculate(&Field::None), expected); } None => unreachable!("should have returned a calculation"), } } } #[derive(Clone, Debug)] struct Reply { data: Vec, } impl Reply { fn new() -> Self { Self { data: Vec::new() } } fn add(&mut self, doc: Document) { self.data.push(doc); } fn len(&self) -> usize { self.data.len() } fn iter(&self) -> impl Iterator { self.data.iter() } } #[cfg(test)] mod replies { use super::*; #[test] fn is_new_empty() { let reply = Reply::new(); assert_eq!(reply.len(), 0, "should have no records"); } #[test] fn can_add_documents() { let mut reply = Reply::new(); let doc = Document::new(); reply.add(doc.clone()); assert_eq!(reply.len(), 1); reply.add(doc.clone()); assert_eq!(reply.len(), 2); } #[test] fn can_retrieve_documents() { let fieldname = Name::english("field"); let mut doc1 = Document::new(); doc1.add_field(fieldname.clone(), "one"); let mut doc2 = Document::new(); doc2.add_field(fieldname.clone(), "two"); let mut reply = Reply::new(); reply.add(doc1); reply.add(doc2); let mut reply_iter = reply.iter(); let mut result1 = reply_iter.next().unwrap(); match result1.get_field(&fieldname) { CalcValue::Value(data) => match data { Field::StaticString(output) => assert_eq!(output, "one"), _ => unreachable!("got {:?}: should have been static string", result1), }, _ => unreachable!("got {:?}, should have been value", result1), } let result2 = reply_iter.next().unwrap(); match result2.get_field(&fieldname) { CalcValue::Value(data) => match data { Field::StaticString(output) => assert_eq!(output, "two"), _ => unreachable!("got {:?}: should have been static string", result2), }, _ => unreachable!("got {:?}, should have been value", result2), } match reply_iter.next() { None => {} Some(_) => unreachable!("should be out of data"), } } } #[derive(Clone, Debug)] struct InternalRecord { data: HashMap, } impl InternalRecord { fn new() -> Self { Self { data: HashMap::new(), } } fn insert(&mut self, id: Uuid, data: F) -> Field where F: Into, { match self.data.insert(id, data.into()) { Some(data) => data.clone(), None => Field::None, } } fn get(&self, id: &Uuid) -> Option<&Field> { self.data.get(id) } fn keys(&self) -> impl Iterator { self.data.keys() } fn is_empty(&self) -> bool { self.data.is_empty() } } #[derive(Clone, Debug)] struct InternalRecords { data: HashMap, } impl InternalRecords { fn new() -> Self { Self { data: HashMap::new(), } } fn insert(&mut self, oid: Oid, record: InternalRecord) -> Option { self.data.insert(oid, record) } fn get(&self, oid: &Oid) -> Option<&InternalRecord> { self.data.get(oid) } fn remove(&mut self, oid: &Oid) -> Option { self.data.remove(oid) } fn iter(&self) -> impl Iterator { self.data.iter() } fn keys(&self) -> impl Iterator { self.data.keys() } fn values(&self) -> impl Iterator { self.data.values() } fn contains_key(&self, oid: &Oid) -> bool { self.data.contains_key(oid) } fn is_empty(&self) -> bool { self.data.is_empty() } fn len(&self) -> usize { self.data.len() } } #[derive(Clone, Debug)] struct Record { names: Names, data: InternalRecord, } impl Record { fn with_data(names: Names, rec: InternalRecord) -> Self { Self { names: names, data: rec, } } fn get(&self, field_id: NT) -> Result where NT: Into, { let id = match self.names.get_id(field_id) { Ok(data) => data, Err(err) => return Err(err), }; match self.data.get(&id) { Some(data) => Ok(data.clone()), None => Err(MTTError::FieldMissingData), } } } #[derive(Clone, Debug)] struct Records { names: Names, data: InternalRecords, } impl Records { fn new(names: Names) -> Self { Self { names: names, data: InternalRecords::new(), } } fn with_data(names: Names, records: InternalRecords) -> Self { Self { names: names, data: records, } } fn insert(&mut self, oid: Oid, record: InternalRecord) -> Option { self.data.insert(oid, record) } fn len(&self) -> usize { self.data.len() } fn iter(&self) -> impl Iterator { RecordIter::new(self) } fn get_internal_records(&self) -> &InternalRecords { &self.data } } struct RecordIter { names: Names, recs: Vec, } impl RecordIter { fn new(records: &Records) -> Self { Self { names: records.names.clone(), recs: records.data.values().cloned().collect(), } } } impl Iterator for RecordIter { type Item = Record; fn next(&mut self) -> Option { match self.recs.pop() { Some(rec) => Some(Record::with_data(self.names.clone(), rec.clone())), None => None, } } } #[derive(Clone, Debug)] struct Document { data: HashMap, } impl Document { fn new() -> Self { Self { data: HashMap::new(), } } fn add_field(&mut self, name: NT, field: CV) where CV: Into, NT: Into, { self.data.insert(name.into(), field.into()); } fn get_field(&self, name: NT) -> &CalcValue where NT: Into, { match self.data.get(&name.into()) { Some(data) => data, None => &CalcValue::None, } } fn get_all(&self) -> Vec<(NameType, Field)> { let mut output = Vec::new(); for (key, value) in self.data.iter() { output.push((key.clone(), value.get(&Field::None))); } output } fn iter(&self) -> impl Iterator { self.data.iter() } fn is_empty(&self) -> bool { self.data.is_empty() } } struct DocIter { storage: Vec<(NameType, Field)>, } impl DocIter { fn new(doc: &Document) -> Self { Self { storage: doc.get_all(), } } } impl Iterator for DocIter { type Item = (NameType, Field); fn next(&mut self) -> Option { self.storage.pop() } } #[cfg(test)] mod documents { use super::*; #[test] fn can_add_static_string() { let mut add = Document::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4().to_string(); add.add_field(name.clone(), data.clone()); let result = add.get_field(&name); match result { CalcValue::Value(holder) => match holder { Field::StaticString(result) => assert_eq!(result, &data), _ => unreachable!("got {:?}: should have received static string", holder), }, _ => unreachable!("got {:?}, should have been value", result), } } fn can_add_uuid() { let mut add = Document::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4(); add.add_field(name.clone(), data.clone()); let result = add.get_field(&name); match result { CalcValue::Value(holder) => match holder { Field::Uuid(result) => assert_eq!(result, &data), _ => unreachable!("got {:?}: should have received static string", holder), }, _ => unreachable!("got {:?}, should have been value", result), } } } #[derive(Clone, Debug)] struct Delete { query: Query, } impl Delete { fn new(query: Query) -> Self { Self { query: query.into(), } } fn get_query(&self) -> &Query { &self.query } } #[derive(Clone, Debug)] struct Update { query: Query, values: Document, } impl Update { fn new(query: Query) -> Self { Self { query: query.into(), values: Document::new(), } } fn get_query(&self) -> &Query { &self.query } fn get_values(&self) -> &Document { &self.values } fn get_values_mut(&mut self) -> &mut Document { &mut self.values } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] struct Oid { oid: Uuid, } impl Oid { fn new() -> Self { Self { oid: Uuid::new_v4(), } } } #[cfg(test)] mod oids { use super::*; #[test] fn are_oids_random() { let count = 10; let mut holder: Vec = Vec::new(); while holder.len() < count { let result = Oid::new(); assert!(!holder.contains(&result)); holder.push(result); } } } #[derive(Debug)] struct Index { data: HashMap>, unique: bool, } impl Index { fn new() -> Self { Self { data: HashMap::new(), unique: false, } } fn new_unique() -> Self { Self { data: HashMap::new(), unique: true, } } fn internal_add(&mut self, field: &Field, oid: Oid) { let storage = match self.data.get_mut(field) { Some(data) => data, None => { let data = HashSet::new(); self.data.insert(field.clone(), data); self.data.get_mut(field).unwrap() } }; storage.insert(oid); } fn add(&mut self, field: Field, oid: Oid) -> Result<(), MTTError> { let oids = match self.data.get_mut(&field) { Some(data) => data, None => { self.data.insert(field.clone(), HashSet::new()); self.data.get_mut(&field).unwrap() } }; if self.unique && oids.len() > 0 { return Err(MTTError::FieldDuplicate); } else { oids.insert(oid); } Ok(()) } fn append(&mut self, index: &Self) { for (value, oids) in index.data.iter() { match self.data.get(value) { Some(data) => { let holder = data.union(oids).cloned().collect(); self.data.insert(value.clone(), holder); } None => { self.data.insert(value.clone(), oids.clone()); } } } } fn get(&self, spec: &Operation) -> Vec { let mut output = Vec::new(); for (field, oids) in self.data.iter() { if spec.validate(field) { for oid in oids.iter() { output.push(oid.clone()); } } } output } fn pull(&self, calc: &Calculation) -> Result, MTTError> { let mut output = HashSet::new(); for (key, value) in self.data.iter() { match calc.calculate(key) { Field::Boolean(data) => { if data { output = output.union(&value).cloned().collect(); } } _ => return Err(MTTError::FieldInvalidType), } } Ok(output) } fn remove(&mut self, field: &Field, oid: &Oid) { match self.data.get_mut(field) { Some(oids) => { oids.remove(oid); if oids.len() == 0 { self.data.remove(field); } } None => {} }; } fn validate(&self, field: &Field) -> Result<(), MTTError> { if self.unique { match self.data.get(field) { Some(_) => return Err(MTTError::FieldDuplicate), None => {} } } Ok(()) } } struct Indexes { data: HashMap, } impl Indexes { fn new(settings: &HashMap) -> Self { let mut output = HashMap::new(); for (key, value) in settings.iter() { output.insert(key.clone(), value.create_index()); } Self { data: output } } fn index_ids(&self) -> HashSet<&Uuid> { self.data.keys().collect::>() } fn get_index(&self, field_id: &Uuid) -> &Index { self.data.get(field_id).unwrap() } fn get_index_mut(&mut self, field_id: &Uuid) -> &mut Index { self.data.get_mut(field_id).unwrap() } fn append(&mut self, indexes: &Self) { for (field_id, index) in indexes.iter() { self.data.get_mut(field_id).unwrap().append(index); } } fn pull(&self, field_id: &Uuid, calc: &Calculation) -> Result, MTTError> { self.data.get(field_id).unwrap().pull(calc) } fn add_to_index(&mut self, field_name: &Uuid, field: Field, oid: Oid) -> Result<(), MTTError> { let index = match self.data.get_mut(field_name) { Some(data) => data, None => return Ok(()), }; index.add(field, oid) } fn remove_from_index(&mut self, field_name: &Uuid, field: &Field, oid: &Oid) { let index = match self.data.get_mut(field_name) { Some(data) => data, None => return, }; index.remove(field, oid); } fn validate(&self, field_name: &Uuid, value: &Field) -> Result<(), MTTError> { match self.data.get(field_name) { Some(index) => match index.validate(value) { Ok(_) => {} Err(err) => return Err(err), }, None => {} } Ok(()) } fn iter(&self) -> impl Iterator { self.data.iter() } fn iter_mut(&mut self) -> impl Iterator { self.data.iter_mut() } } #[cfg(test)] mod indexes { use super::*; fn get_fields(count: usize) -> Vec { let mut output = Vec::new(); while output.len() < count { let field: Field = Uuid::new_v4().into(); if !output.contains(&field) { output.push(field); } } output } fn get_oids(count: usize) -> Vec { let mut output = Vec::new(); while output.len() < count { let oid = Oid::new(); if !output.contains(&oid) { output.push(oid); } } output } #[test] fn add_to_index() { let mut index = Index::new(); let count = 3; let fields = get_fields(count); let oids = get_oids(count); for i in 0..count { index.add(fields[i].clone(), oids[i].clone()); } for i in 0..count { let spec = Operation::new("stuff".to_string(), Operand::Equal, fields[i].clone()); let result = index.get(&spec); assert_eq!(result.len(), 1); assert_eq!(result[0], oids[i]); } } #[test] fn index_can_handle_multiple_entries() { let mut index = Index::new(); let count = 3; let fields = get_fields(1); let oids = get_oids(count); for i in 0..count { index.add(fields[0].clone(), oids[i].clone()); } let spec = Operation::new("unimportant".to_string(), Operand::Equal, fields[0].clone()); let result = index.get(&spec); assert_eq!(result.len(), 3); for oid in oids { assert!(result.contains(&oid)); } } #[test] fn can_remove_oid() { let mut index = Index::new(); let count = 3; let pos = 1; let fields = get_fields(1); let oids = get_oids(count); for i in 0..count { index.add(fields[0].clone(), oids[i].clone()); } index.remove(&fields[0], &oids[pos]); let spec = Operation::new("x".to_string(), Operand::Equal, fields[0].clone()); let result = index.get(&spec); assert!(!result.contains(&oids[pos]), "should have removed oid"); } #[test] fn are_empty_indexes_removed() { let mut index = Index::new(); let field: Field = Uuid::new_v4().into(); let oid = Oid::new(); index.add(field.clone(), oid.clone()); index.remove(&field, &oid); assert_eq!(index.data.len(), 0); } #[test] fn do_unique_indexes_error_on_duplicates() { let mut index = Index::new_unique(); let field: Field = "fred".into(); let oids = get_oids(2); index.add(field.clone(), oids[0].clone()).unwrap(); match index.add(field.clone(), oids[0].clone()) { Ok(_) => unreachable!("should have been an error"), Err(err) => match err { MTTError::FieldDuplicate => {} _ => unreachable!("got {:?}: should have been duplicate field", err), }, } } #[test] fn index_returns_validate() { let mut index = Index::new(); let field: Field = "stuff".into(); let oid = Oid::new(); index.add(field.clone(), oid).unwrap(); match index.validate(&field) { Ok(_) => {} Err(err) => unreachable!("got {:?}: should have returned without issue", err), } } #[test] fn unique_return_duplicate_error() { let mut index = Index::new_unique(); let field: Field = "fred".into(); let oid = Oid::new(); index.add(field.clone(), oid).unwrap(); match index.validate(&field) { Ok(_) => unreachable!("should have gotten a duplication error"), Err(err) => match err { MTTError::FieldDuplicate => {} _ => unreachable!("got {:?}: should have been duplicate field", err), }, } } } struct DocumentFile { docdef: DocDef, docs: InternalRecords, indexes: Indexes, queue: Queue, routes: HashMap, rx: Receiver, } impl DocumentFile { fn new( queue: Queue, rx: Receiver, docdef: DocDef, routes: HashMap, ) -> Self { Self { docdef: docdef.clone(), docs: InternalRecords::new(), indexes: docdef.create_indexes(), queue: queue, routes: routes, rx: rx, } } fn start(mut queue: Queue, msg: Message) { let (tx, rx) = channel(); let action = msg.get_action(); let docdef = match action { MsgAction::Create(data) => data.clone(), _ => unreachable!("got {:?}: should have been a create message", action), }; let names = docdef.get_document_names(); let id = queue.add_sender(tx); let reg_msg = Register::new(id, RegMsg::AddDocName(names.clone())); let rmsg = msg.response(reg_msg.clone()); queue.send(rmsg.clone()).unwrap(); let name_result = rx.recv().unwrap(); let name_id = match name_result.get_action() { MsgAction::Register(data) => match data.get_msg() { RegMsg::DocumentNameID(data) => data, RegMsg::Error(err) => { queue.remove_sender(&id); queue.send(msg.response(err.clone())).unwrap(); return; } _ => unreachable!("should only return a name id or an error"), }, _ => unreachable!("should only return a name id or an error"), }; let mut route_action: HashMap = HashMap::new(); for path_action in docdef.iter_routes() { let request = reg_msg.response(RegMsg::AddRoute(path_action.path())); let add_route = rmsg.response(request); queue.send(add_route).unwrap(); let result = rx.recv().unwrap(); let route_id = match result.get_action() { MsgAction::Register(data) => match data.get_msg() { RegMsg::RouteID(data) => data, RegMsg::Error(err) => { queue.remove_sender(&id); queue.send(msg.response(err.clone())).unwrap(); return; } _ => unreachable!("should only return a route id or an error"), }, _ => unreachable!("should only return a route id or an error"), }; route_action.insert(route_id.clone(), path_action.doc_function()); } let mut doc = DocumentFile::new(queue.clone(), rx, docdef, route_action); spawn(move || { doc.listen(); }); let reply = msg.response(Reply::new()); queue.send(reply.clone()).unwrap(); } fn listen(&mut self) { loop { let msg = self.rx.recv().unwrap(); let route: Route = msg.get_path().try_into().unwrap(); for (route_id, doc_func) in self.routes.clone().iter() { if route == route_id.into() { match doc_func { DocFuncType::Add => self.add_document(&msg), DocFuncType::Delete => self.delete(&msg), DocFuncType::Query => self.query(&msg), DocFuncType::Show => self.queue.send(msg.response(Reply::new())).unwrap(), DocFuncType::Update => self.update(&msg), DocFuncType::ExistingQuery(action) => self.existing_query(&msg, action), _ => {} } } } } } fn get_docdef(&self) -> &DocDef { &self.docdef } fn validate(&self, field_name: NT, value: &Field) -> Result where NT: Into, { let field_id = match self.docdef.get_field_id(field_name) { Ok(data) => data, Err(err) => return Err(err), }; let output = match self.docdef.validate(field_id.clone(), value) { Ok(data) => data, Err(err) => return Err(err), }; match self.indexes.validate(&field_id, &output) { Ok(_) => {} Err(err) => return Err(err), } Ok(output) } fn add_field_to_error(key: String, err: MTTError) -> MTTError { match err { MTTError::DocumentFieldMissing(_) => MTTError::DocumentFieldMissing(key), _ => err.clone(), } } fn add_to_index(&mut self, field_name: NT, field: Field, oid: Oid) where NT: Into, { let field_id = self.docdef.get_field_id(field_name).unwrap(); self.indexes.add_to_index(&field_id, field, oid).unwrap(); } fn remove_from_index(&mut self, field_name: NT, field: &Field, oid: &Oid) where NT: Into, { let field_id = self.docdef.get_field_id(field_name).unwrap(); self.indexes.remove_from_index(&field_id, field, oid); } fn add_document(&mut self, msg: &Message) { let addition = match msg.get_action() { MsgAction::Addition(data) => data, _ => return, }; let mut holder = InternalRecord::new(); for (name_id, value) in addition.iter() { let field_id = match self.docdef.get_field_id(name_id) { Ok(id) => id, Err(err) => { let reply = msg.response(err); self.queue.send(reply).unwrap(); return; } }; holder.insert(field_id.clone(), value.get(&Field::None)); } for field_id in self.docdef.field_ids().iter() { let value = match holder.get(field_id) { Some(data) => data, None => &Field::None, }; let corrected = match self.validate(field_id, value) { Ok(data) => data, Err(err) => { let reply = msg.response(err); self.queue.send(reply).unwrap(); return; } }; holder.insert(field_id.clone(), corrected.clone()); } let mut records = Records::new(self.docdef.get_field_names().clone()); if !holder.is_empty() { let mut oid = Oid::new(); while self.docs.contains_key(&oid) { oid = Oid::new(); } for (field_id, oids) in self.indexes.iter_mut() { let value = holder.get(field_id).unwrap(); oids.internal_add(value, oid.clone()); } self.docs.insert(oid.clone(), holder.clone()); records.insert(oid, holder); } self.queue.send(msg.response(records.clone())).unwrap(); self.queue .send(msg.response(MsgAction::OnAddition(records))) .unwrap(); } fn delete(&mut self, msg: &Message) { let delete = match msg.get_action() { MsgAction::Delete(data) => data, _ => unreachable!("should always be delete action"), }; let records = match self.run_query(delete.get_query()) { Ok(data) => data, Err(err) => { let reply = msg.response(err); self.queue.send(reply).unwrap(); return; } }; for (oid, record) in records.iter() { for (field_id, index) in self.indexes.iter_mut() { index.remove(record.get(field_id).unwrap(), oid); } self.docs.remove(oid); } let rec = Records::with_data(self.docdef.get_field_names().clone(), records); self.queue.send(msg.response(rec.clone())).unwrap(); self.queue .send(msg.response(MsgAction::OnDelete(rec))) .unwrap(); } fn run_query(&self, query: &Query) -> Result { let indexed_ids = self.indexes.index_ids(); let mut indexed: HashMap = HashMap::new(); let mut unindexed: HashMap = HashMap::new(); for (field, data) in query.iter() { let id = match self.docdef.get_field_id(field) { Ok(fid) => fid, Err(err) => return Err(err), }; if indexed_ids.contains(&id) { indexed.insert(id, data.clone()); } else { unindexed.insert(id, data.clone()); } } let mut oids: HashSet = self.docs.keys().cloned().collect(); for (field_id, calculation) in indexed.iter() { let holder = match self.indexes.pull(field_id, calculation) { Ok(data) => data, Err(err) => return Err(err), }; oids = oids.intersection(&holder).cloned().collect(); } let mut records = InternalRecords::new(); for oid in oids.iter() { records.insert(oid.clone(), self.docs.get(oid).unwrap().clone()); } let holder = oids.clone(); for (oid, record) in records.iter() { for (field_id, calc) in unindexed.iter() { match calc.calculate(record.get(field_id).unwrap()) { Field::Boolean(data) => { if !data { oids.remove(oid); break; } } _ => { oids.remove(oid); break; } } } } let removals = holder.difference(&oids); for oid in removals { records.remove(oid); } Ok(records) } fn query(&self, msg: &Message) { let query = match msg.get_action() { MsgAction::Query(data) => data, _ => unreachable!("should receive a query"), }; let records = match self.run_query(query) { Ok(data) => data, Err(err) => { let reply = msg.response(err); self.queue.send(reply).unwrap(); return; } }; let recs = Records::with_data(self.docdef.get_field_names().clone(), records); self.queue.send(msg.response(recs.clone())).unwrap(); self.queue .send(msg.response(MsgAction::OnQuery(recs))) .unwrap(); } fn run_update( &mut self, original: &InternalRecords, update: &Update, msg: &Message, ) -> Result { let mut changes: HashMap = HashMap::new(); for (key, value) in update.get_values().iter() { let field_id = match self.docdef.get_field_id(key) { Ok(data) => data, Err(err) => return Err(err), }; changes.insert(field_id, value); } let mut indexes = self.docdef.create_indexes(); let mut updates = InternalRecords::new(); for (oid, record) in original.iter() { let mut holder = record.clone(); for (field_id, value) in changes.iter() { let field = value.get(holder.get(field_id).unwrap()); let correction = match self.validate(field_id, &field) { Ok(data) => data, Err(err) => return Err(err), }; holder.insert(field_id.clone(), correction.clone()); match indexes.add_to_index(&field_id, correction, oid.clone()) { Ok(_) => {} Err(err) => return Err(err), } } updates.insert(oid.clone(), holder); } for (oid, new_rec) in updates.iter() { let old_rec = original.get(oid).unwrap(); for (field_id, index) in self.indexes.iter_mut() { index.remove(old_rec.get(field_id).unwrap(), oid); index.add(new_rec.get(field_id).unwrap().clone(), oid.clone()); } self.docs.insert(oid.clone(), new_rec.clone()); } let recs = Records::with_data(self.docdef.get_field_names().clone(), updates); self.queue .send(msg.response(MsgAction::OnUpdate(recs.clone()))) .unwrap(); Ok(recs) } fn update(&mut self, msg: &Message) { let update = match msg.get_action() { MsgAction::Update(data) => data, _ => unreachable!("should receive a update"), }; let original = match self.run_query(update.get_query()) { Ok(result) => result, Err(err) => { let reply = msg.response(err); self.queue.send(reply).unwrap(); return; } }; let data = match self.run_update(&original, update, msg) { Ok(output) => output, Err(err) => { let reply = msg.response(err); self.queue.send(reply).unwrap(); return; } }; self.queue.send(msg.response(data)).unwrap(); } fn existing_query(&mut self, msg: &Message, action: &MsgAction) { let recs = match msg.get_action() { MsgAction::OnQuery(data) => data, _ => unreachable!("should only receive on messages"), }; match action { MsgAction::Update(change) => self .run_update(recs.get_internal_records(), change, msg) .unwrap(), _ => panic!("should not get here"), }; } } #[cfg(test)] mod document_files { use super::{support_test::TIMEOUT, *}; use std::sync::mpsc::RecvTimeoutError; fn standard_paths() -> Vec { [ Path::new(Include::All, Include::All, Include::Some(Action::Records)), Path::new(Include::All, Include::All, Include::Some(Action::Reply)), Path::new(Include::All, Include::All, Include::Some(Action::Error)), ] .to_vec() } struct TestDocument { docdef: DocDef, queue: Queue, sender_id: Uuid, rx: Receiver, } impl TestDocument { fn new(field_types: Vec) -> Self { let doc_name = Name::english(Uuid::new_v4().to_string().as_str()); let mut docdef = DocDef::new(doc_name.clone()); let mut count = 0; for field_type in field_types.iter() { docdef.add_field( Name::english(format!("field{}", count).as_str()), field_type.clone(), ); count += 1; } let (tx, rx) = channel(); let mut queue = Queue::new(); let id = queue.add_sender(tx); Self { docdef: docdef, queue: queue, sender_id: id, rx: rx, } } fn get_docdef(&self) -> &DocDef { &self.docdef } fn get_docdef_mut(&mut self) -> &mut DocDef { &mut self.docdef } fn get_queue(&mut self) -> Queue { self.queue.clone() } fn get_receiver(&self) -> &Receiver { &self.rx } fn get_name_id(&self) -> Uuid { let reg_request = RegMsg::GetNameID(self.docdef.get_document_names()[0].clone()); let reg_msg = Register::new(self.get_sender_id(), reg_request); let msg = Message::new(NameType::None, reg_msg); self.queue.send(msg).unwrap(); let result = self.rx.recv().unwrap(); match result.get_action() { MsgAction::Register(data) => match data.get_msg() { RegMsg::DocumentNameID(output) => output.clone(), _ => unreachable!("should return a name id"), }, _ => unreachable!("should return a name id"), } } fn get_sender_id(&self) -> Uuid { self.sender_id.clone() } fn send(&self, action: A) -> Result<(), MTTError> where A: Into, { let msg = Message::new(self.docdef.get_document_names()[0].clone(), action); self.queue.send(msg) } fn start(&mut self, routes: Vec) { let msg = Message::new( self.docdef.get_document_names()[0].clone(), self.docdef.clone(), ); DocumentFile::start(self.queue.clone(), msg); for route in routes.iter() { let request = Register::new(self.sender_id.clone(), RegMsg::AddRoute(route.clone())); let add_route = Message::new(NameType::None, request); self.queue.send(add_route).unwrap(); self.rx.recv().unwrap(); } } fn populate(&self, data: Vec) { let mut add = Addition::new(); let mut count = 0; for item in data.iter() { add.add_field( Name::english(format!("field{}", count).as_str()), item.clone(), ); count += 1; } self.send(add).unwrap(); match self.rx.recv_timeout(TIMEOUT) { Ok(_) => {} // eats the addition response. Err(err) => match err { RecvTimeoutError::Timeout => {} _ => unreachable!("got {}, should have been ok or time out", err), }, } } } impl From for TestDocument { fn from(value: DocDef) -> Self { let (tx, rx) = channel(); let mut queue = Queue::new(); let id = queue.add_sender(tx); Self { docdef: value, queue: queue, sender_id: id, rx: rx, } } } #[test] fn does_not_respond_to_create() { let name = Name::english("quiet"); let docdef = DocDef::new(name.clone()); let mut test_doc: TestDocument = docdef.into(); let alt = Name::english("alternate"); test_doc.start(standard_paths()); let docdef = DocDef::new(alt); let msg = Message::new(name.clone(), docdef); test_doc.get_queue().send(msg).unwrap(); match test_doc.get_receiver().recv_timeout(TIMEOUT) { Ok(msg) => unreachable!("should not receive: {:?}", msg), Err(err) => match err { RecvTimeoutError::Timeout => {} _ => unreachable!("should have timed out"), }, } } #[test] fn does_document_respond_to_requests() { let name = Name::english("listen"); let docdef = DocDef::new(name.clone()); let mut test_doc: TestDocument = docdef.into(); test_doc.start(standard_paths()); let queue = test_doc.get_queue(); let msg_actions = [ MsgAction::Addition(Addition::new()), MsgAction::Delete(Delete::new(Query::new())), MsgAction::Query(Query::new().into()), MsgAction::Show, MsgAction::Update(Update::new(Query::new())), ]; for msg_action in msg_actions.iter() { let msg = Message::new(name.clone(), msg_action.clone()); queue.send(msg.clone()).unwrap(); let result = match test_doc.get_receiver().recv_timeout(TIMEOUT) { Ok(data) => data.clone(), Err(err) => unreachable!("for {:?} got {:?}", msg_action, err), }; assert_eq!( result.get_message_id(), msg.get_message_id(), "for {:?} response and reply ids should equal", msg_action ); match result.get_action() { MsgAction::Reply(data) => { assert_eq!(data.len(), 0, "for {:?} got {:?}", msg_action, result) } MsgAction::Records(data) => { assert_eq!(data.len(), 0, "for {:?} got {:?}", msg_action, result) } _ => unreachable!( "for {:?} got {:?}: should have received a reply", msg_action, result.get_action() ), } } } #[test] fn does_not_respond_to_other_document_requests() { let name = Name::english("quiet"); let alt = Name::english("alternate"); let docdef = DocDef::new(name.clone()); let mut test_doc: TestDocument = docdef.into(); test_doc.start(standard_paths()); let queue = test_doc.get_queue(); let reg_msg = Register::new( test_doc.get_sender_id(), RegMsg::AddDocName([alt.clone()].to_vec()), ); let setup = Message::new(NameType::None, reg_msg.clone()); queue.send(setup).unwrap(); test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let msg_actions = [ MsgAction::Addition(Addition::new()), MsgAction::Create(DocDef::new(name.clone())), MsgAction::Delete(Delete::new(Query::new())), MsgAction::Query(Query::new().into()), MsgAction::Show, MsgAction::Update(Update::new(Query::new())), ]; let mut msgs: HashMap = HashMap::new(); for msg_action in msg_actions.iter() { let msg = Message::new(alt.clone(), msg_action.clone()); msgs.insert(msg.get_message_id().clone(), msg_action.clone()); queue.send(msg).unwrap(); } match test_doc.get_receiver().recv_timeout(TIMEOUT) { Ok(msg) => unreachable!( "for {:?} should not receive: {:?}", msgs.get(msg.get_message_id()).unwrap(), msg ), Err(err) => match err { RecvTimeoutError::Timeout => {} _ => unreachable!("got {:?}, should have timed out", err), }, } } #[test] fn query_sends_on_query_message() { let count = 5; let mut data: HashSet = HashSet::new(); while data.len() < count { let field: Field = Uuid::new_v4().into(); data.insert(field); } let mut test_doc = TestDocument::new([FieldType::Uuid].to_vec()); let doc_name = test_doc.get_docdef().get_document_names()[0].clone(); let queue = test_doc.get_queue(); let routes = [Path::new( Include::All, Include::All, Include::Some(Action::OnQuery), )] .to_vec(); test_doc.start(routes); let name_id: NameType = test_doc.get_name_id().into(); for item in data.iter() { test_doc.populate([item.clone()].to_vec()); } let msg = Message::new(doc_name.clone(), Query::new()); queue.send(msg.clone()).unwrap(); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); assert_eq!( result.get_message_id(), msg.get_message_id(), "message ids should match" ); assert_eq!( result.get_document_id(), &name_id, "document name ids should match" ); match result.get_action() { MsgAction::OnQuery(output) => { assert_eq!( output.len(), count, "wrong number of entries: got {:?}", output ); for rec in output.iter() { assert!(data.contains(&rec.get(Name::english("field0")).unwrap())); } } _ => unreachable!("should never get here"), } } #[test] fn send_on_addition_message() { let data: Field = Uuid::new_v4().into(); let field_name = Name::english("field0"); let mut test_doc = TestDocument::new([FieldType::Uuid].to_vec()); let doc_name = test_doc.get_docdef().get_document_names()[0].clone(); let queue = test_doc.get_queue(); let routes = vec![Path::new( Include::All, Include::All, Include::Some(Action::OnAddition), )]; test_doc.start(routes); let name_id: NameType = test_doc.get_name_id().into(); let mut add = Addition::new(); add.add_field(field_name.clone(), data.clone()); let msg = Message::new(doc_name.clone(), add); queue.send(msg.clone()).unwrap(); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); assert_eq!( result.get_message_id(), msg.get_message_id(), "message ids should match" ); assert_eq!( result.get_document_id(), &name_id, "document name ids should match" ); match result.get_action() { MsgAction::OnAddition(output) => { assert_eq!(output.len(), 1, "wrong number of entries: got {:?}", output); for rec in output.iter() { assert_eq!(rec.get(Name::english("field0")).unwrap(), data); } } _ => unreachable!("should never get here"), } } #[test] fn sends_on_delete_message() { let count = 5; let mut data: HashSet = HashSet::new(); while data.len() < count { let field: Field = Uuid::new_v4().into(); data.insert(field); } let mut test_doc = TestDocument::new([FieldType::Uuid].to_vec()); let doc_name = test_doc.get_docdef().get_document_names()[0].clone(); let queue = test_doc.get_queue(); let routes = [Path::new( Include::All, Include::All, Include::Some(Action::OnDelete), )] .to_vec(); test_doc.start(routes); let name_id: NameType = test_doc.get_name_id().into(); for item in data.iter() { test_doc.populate([item.clone()].to_vec()); } let msg = Message::new(doc_name.clone(), Delete::new(Query::new())); queue.send(msg.clone()).unwrap(); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); assert_eq!( result.get_message_id(), msg.get_message_id(), "message ids should match" ); assert_eq!( result.get_document_id(), &name_id, "document name ids should match" ); match result.get_action() { MsgAction::OnDelete(output) => { assert_eq!( output.len(), count, "wrong number of entries: got {:?}", output ); for rec in output.iter() { assert!(data.contains(&rec.get(Name::english("field0")).unwrap())); } } _ => unreachable!("should never get here"), } } #[test] fn sends_on_update_message() { let count = 5; let field_name = Name::english("field0"); let mut data: HashSet = HashSet::new(); while data.len() < count { let field: Field = Uuid::new_v4().into(); data.insert(field); } let mut test_doc = TestDocument::new([FieldType::Uuid].to_vec()); let doc_name = test_doc.get_docdef().get_document_names()[0].clone(); let queue = test_doc.get_queue(); let routes = [Path::new( Include::All, Include::All, Include::Some(Action::OnUpdate), )] .to_vec(); test_doc.start(routes); let name_id: NameType = test_doc.get_name_id().into(); for item in data.iter() { test_doc.populate([item.clone()].to_vec()); } let mut update = Update::new(Query::new()); update .get_values_mut() .add_field(field_name.clone(), Uuid::nil()); let msg = Message::new(doc_name.clone(), update); queue.send(msg.clone()).unwrap(); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); assert_eq!( result.get_message_id(), msg.get_message_id(), "message ids should match" ); assert_eq!( result.get_document_id(), &name_id, "document name ids should match" ); match result.get_action() { MsgAction::OnUpdate(output) => { assert_eq!( output.len(), count, "wrong number of entries: got {:?}", output ); for rec in output.iter() { assert_eq!(rec.get(&field_name).unwrap(), Uuid::nil().into()); } } _ => unreachable!("should never get here"), } } #[test] fn can_document_be_added() { let doc_name = Name::english("document"); let mut docdef = DocDef::new(doc_name.clone()); let name = Name::english("field"); let data = Uuid::new_v4(); docdef.add_field(name.clone(), FieldType::Uuid); let mut test_doc: TestDocument = docdef.clone().into(); test_doc.start(standard_paths()); let queue = test_doc.get_queue(); let mut new_doc = Addition::new(); new_doc.add_field(name.clone(), data.clone()); let testing = |msg: Message| { queue.send(msg.clone()).unwrap(); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); match result.get_action() { MsgAction::Records(output) => { assert_eq!(output.len(), 1); for rec in output.iter() { let holder = rec.get(&name).unwrap(); match holder { Field::Uuid(field_data) => assert_eq!(field_data, data), _ => unreachable!("got {:?}, should have been uuid", holder), } } } _ => unreachable!( "\n\ngot {:?}\n\nfor {:?}\n\nshould have been records", result, msg ), } }; testing(Message::new(doc_name.clone(), new_doc)); testing(Message::new(doc_name.clone(), Query::new())); } #[test] fn can_add_multiple_documents() { let doc_name = Name::english("multiple"); let mut docdef = DocDef::new(doc_name.clone()); let name = Name::english("count"); docdef.add_field(name.clone(), FieldType::Integer); let mut test_doc: TestDocument = docdef.clone().into(); test_doc.start(standard_paths()); let queue = test_doc.get_queue(); let count = 5; for i in 0..count { let mut new_doc = Addition::new(); new_doc.add_field(name.clone(), i); queue.send(Message::new(doc_name.clone(), new_doc)).unwrap(); test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); } queue.send(Message::new(doc_name, Query::new())); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); let mut entries: HashSet = (0..count).collect(); match action { MsgAction::Records(output) => { let entry_count: usize = count.try_into().unwrap(); assert_eq!( output.len(), entry_count, "should have the same number of entries" ); for record in output.iter() { let holder = record.get(&name).unwrap(); let data = match holder { Field::Integer(item) => item.clone(), _ => unreachable!("got {:?}, should have been integer", holder), }; assert!( entries.contains(&data), "did not find {:?} in {:?}", data, entries ); entries.remove(&data); } } _ => unreachable!("\n\ngot {:?}\n\nshould have been records", action), } assert!(entries.is_empty(), "did not use {:?}", entries); } #[test] fn errors_on_wrong_field_name() { let mut test_doc = TestDocument::new(Vec::new()); test_doc.start(standard_paths()); let queue = test_doc.get_queue(); let name = Name::english("bad"); let mut addition = Addition::new(); addition.add_field(name.clone(), "doesn't matter"); queue .send(Message::new( test_doc.get_docdef().get_document_names()[0].clone(), addition, )) .unwrap(); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); match result.get_action() { MsgAction::Error(err) => match err { MTTError::NameNotFound(data) => assert_eq!(data, &name), _ => unreachable!("got {:?}: should have been document field not found.", err), }, _ => unreachable!("got {:?}: should have been an error", result.get_action()), } } #[test] fn errors_on_wrong_field_type() { let mut test_doc = TestDocument::new([FieldType::Uuid].to_vec()); test_doc.start(standard_paths()); let queue = test_doc.get_queue(); let mut addition = Addition::new(); addition.add_field(Name::english("field0"), "string"); queue.send(Message::new( test_doc.get_docdef().get_document_names()[0].clone(), addition, )); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); match result.get_action() { MsgAction::Error(err) => match err { MTTError::DocumentFieldWrongDataType(expected, got) => { assert_eq!(got, &FieldType::StaticString); assert_eq!(expected, &FieldType::Uuid); } _ => unreachable!( "got {:?}: should have been document field data mismatch.", err ), }, _ => unreachable!("got {:?}: should have been an error", result.get_action()), } } #[test] fn errors_on_missing_fields() { let mut test_doc = TestDocument::new([FieldType::Integer, FieldType::Integer].to_vec()); test_doc.start(standard_paths()); let queue = test_doc.get_queue(); let mut addition = Addition::new(); addition.add_field(Name::english("field0"), 1); queue.send(Message::new( test_doc.get_docdef().get_document_names()[0].clone(), addition, )); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); match result.get_action() { MsgAction::Error(err) => match err { MTTError::InvalidNone => {} _ => unreachable!("got {:?}: should have been document field missing", err), }, _ => unreachable!("got {:?}: should have been an error", result.get_action()), } } #[test] fn does_query_return_related_entries() { let mut test_doc = TestDocument::new([FieldType::Integer].to_vec()); test_doc.start(standard_paths()); let queue = test_doc.get_queue(); let count = 5; let expected = 3; for i in 0..count { test_doc.populate([i.into()].to_vec()); } let mut query = Query::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value(expected.clone()); calc.add_value(CalcValue::Existing(FieldType::Integer)); let mut query = Query::new(); query.add(Name::english("field0"), calc); queue.send(Message::new( test_doc.get_docdef().get_document_names()[0].clone(), query, )); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(data) => { assert_eq!( data.len(), 1, "should return one entry containing {:?} got:\n{:?}", expected, action ); for doc in data.iter() { assert_eq!(doc.get(&Name::english("field0")).unwrap(), expected.into()); } } _ => unreachable!("got {:?}: should have been a reply", action), } } #[test] fn gets_all_documents_in_query() { let mut test_doc = TestDocument::new([FieldType::Integer].to_vec()); test_doc.start(standard_paths()); let queue = test_doc.get_queue(); let data = 1; let count = 5; for i in 0..count { let holder: i128 = (i + count).try_into().unwrap(); test_doc.populate([holder.into()].to_vec()); test_doc.populate([data.into()].to_vec()); } let mut calc = Calculation::new(Operand::Equal); calc.add_value(data.clone()); calc.add_value(CalcValue::Existing(FieldType::Integer)); let mut query = Query::new(); query.add(Name::english("field0"), calc); queue.send(Message::new( test_doc.get_docdef().get_document_names()[0].clone(), query, )); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(docs) => { assert_eq!( docs.len(), count, "should return one entry containing {:?} got:\n{:?}", data, action ); for doc in docs.iter() { assert_eq!(doc.get(&Name::english("field0")).unwrap(), data.into()); } } _ => unreachable!("got {:?}: should have been a reply", action), } } #[test] fn query_should_work_with_multiple_fields() { let mut doc = TestDocument::new([FieldType::StaticString, FieldType::StaticString].to_vec()); doc.start(standard_paths()); let values = [ ["a".into(), "a".into()].to_vec(), ["a".into(), "b".into()].to_vec(), ["b".into(), "a".into()].to_vec(), ["b".into(), "b".into()].to_vec(), ]; for value in values.iter() { doc.populate(value.clone()); } let mut query = Query::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value("a"); calc.add_value(CalcValue::Existing(FieldType::StaticString)); query.add(Name::english("field0"), calc); let mut calc = Calculation::new(Operand::Equal); calc.add_value("b"); calc.add_value(CalcValue::Existing(FieldType::StaticString)); query.add(Name::english("field1"), calc); doc.send(query).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(data) => { let afield: Field = "a".into(); let bfield: Field = "b".into(); assert_eq!(data.len(), 1, "should return one entry:\n{:?}", action); for doc in data.iter() { assert_eq!(doc.get(&Name::english("field0")).unwrap(), afield); assert_eq!(doc.get(&Name::english("field1")).unwrap(), bfield); } } _ => unreachable!("got {:?}: should have been a reply", action), } } #[test] fn query_should_work_with_multiple_inexed_fields() { let mut doc = TestDocument::new([FieldType::StaticString, FieldType::StaticString].to_vec()); let docdef = doc.get_docdef_mut(); docdef.add_index(&Name::english("field0"), IndexType::Index); docdef.add_index(&Name::english("field1"), IndexType::Index); doc.start(standard_paths()); let values = [ ["a".into(), "a".into()].to_vec(), ["a".into(), "b".into()].to_vec(), ["b".into(), "a".into()].to_vec(), ["b".into(), "b".into()].to_vec(), ]; for value in values.iter() { doc.populate(value.clone()); } let mut query = Query::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value("a"); calc.add_value(CalcValue::Existing(FieldType::StaticString)); query.add(Name::english("field0"), calc); let mut calc = Calculation::new(Operand::Equal); calc.add_value("b"); calc.add_value(CalcValue::Existing(FieldType::StaticString)); query.add(Name::english("field1"), calc); doc.send(query).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(data) => { let afield: Field = "a".into(); let bfield: Field = "b".into(); assert_eq!(data.len(), 1, "should return one entry:\n{:?}", action); for doc in data.iter() { assert_eq!(doc.get(&Name::english("field0")).unwrap(), afield); assert_eq!(doc.get(&Name::english("field1")).unwrap(), bfield); } } _ => unreachable!("got {:?}: should have been a reply", action), } } #[test] fn query_should_work_with_mixed_inexed_fields() { let mut doc = TestDocument::new([FieldType::StaticString, FieldType::StaticString].to_vec()); let docdef = doc.get_docdef_mut(); docdef.add_index(&Name::english("field0"), IndexType::Index); doc.start(standard_paths()); let values = [ ["a".into(), "a".into()].to_vec(), ["a".into(), "b".into()].to_vec(), ["b".into(), "a".into()].to_vec(), ["b".into(), "b".into()].to_vec(), ]; for value in values.iter() { doc.populate(value.clone()); } let mut query = Query::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value("a"); calc.add_value(CalcValue::Existing(FieldType::StaticString)); query.add(Name::english("field0"), calc); let mut calc = Calculation::new(Operand::Equal); calc.add_value("b"); calc.add_value(CalcValue::Existing(FieldType::StaticString)); query.add(Name::english("field1"), calc); doc.send(query).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(data) => { let afield: Field = "a".into(); let bfield: Field = "b".into(); assert_eq!(data.len(), 1, "should return one entry:\n{:?}", action); for doc in data.iter() { assert_eq!(doc.get(&Name::english("field0")).unwrap(), afield); assert_eq!(doc.get(&Name::english("field1")).unwrap(), bfield); } } _ => unreachable!("got {:?}: should have been a reply", action), } } #[test] fn errors_on_bad_field_name() { let mut doc = TestDocument::new(Vec::new()); doc.start(standard_paths()); let doc_name = doc.get_docdef().get_document_names()[0].clone(); let queue = doc.get_queue(); let rx = doc.get_receiver(); let field_name = Name::english("wrong"); let mut query = Query::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value("something"); query.add(field_name.clone(), calc); let msg = Message::new(doc_name, query); queue.send(msg).unwrap(); let result = rx.recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(data) => match data { MTTError::NameNotFound(output) => assert_eq!(output, &field_name), _ => unreachable!("got {:?}: should been field not found", data), }, _ => unreachable!("got {:?}: should have been a error", action), } } #[test] fn errors_on_bad_field_type() { let mut doc = TestDocument::new([FieldType::Uuid].to_vec()); doc.start(standard_paths()); doc.populate([Uuid::nil().into()].to_vec()); let mut calc = Calculation::new(Operand::Equal); calc.add_value("notUUID"); calc.add_value(CalcValue::Existing(FieldType::Uuid)); let mut query = Query::new(); query.add(Name::english("field0"), calc); doc.send(query).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(data) => { assert_eq!(data.len(), 0, "should return one entry:\n{:?}", action); } _ => unreachable!("got {:?}: should have been a reply", action), } } #[test] fn errors_on_bad_field_type_with_index() { let mut doc = TestDocument::new([FieldType::Uuid].to_vec()); doc.get_docdef_mut() .add_index(&Name::english("field0"), IndexType::Index); doc.start(standard_paths()); doc.populate([Uuid::nil().into()].to_vec()); let mut calc = Calculation::new(Operand::Equal); calc.add_value("notUUID"); let mut query = Query::new(); query.add(Name::english("field0"), calc); doc.send(query).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(data) => match data { MTTError::FieldInvalidType => {} _ => unreachable!("got {:?}: should been invalid field type", data), }, _ => unreachable!("got {:?}: should have been a error", action), } } #[test] fn can_use_default_values() { let doc_name = Name::english("default"); let mut docdef = DocDef::new(doc_name.clone()); let field_name = Name::english("holder"); docdef.add_field(field_name.clone(), FieldType::StaticString); docdef.set_default(&field_name, FieldType::StaticString); let mut test_doc: TestDocument = docdef.into(); test_doc.start(standard_paths()); let queue = test_doc.get_queue(); let rx = test_doc.get_receiver(); let new_doc = Addition::new(); let msg = Message::new(doc_name, new_doc); queue.send(msg).unwrap(); let result = rx.recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(docs) => { assert_eq!(docs.len(), 1); for doc in docs.iter() { let expected: Field = "".into(); assert_eq!(doc.get(&field_name).unwrap(), expected); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } } #[test] fn can_a_default_value_be_set() { let doc_name = Name::english("assigned"); let mut docdef = DocDef::new(doc_name.clone()); let field_name = Name::english("id"); docdef.add_field(field_name.clone(), FieldType::Uuid); docdef.set_default(&field_name, Uuid::nil()); let mut test_doc: TestDocument = docdef.into(); test_doc.start(standard_paths()); let queue = test_doc.get_queue(); let rx = test_doc.get_receiver(); let new_doc = Addition::new(); let msg = Message::new(doc_name, new_doc); queue.send(msg).unwrap(); let result = rx.recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(docs) => { assert_eq!(docs.len(), 1); for doc in docs.iter() { let expected: Field = Uuid::nil().into(); assert_eq!(doc.get(&field_name).unwrap(), expected); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } } #[test] fn can_default_values_be_overridden() { let doc_name = Name::english("assigned"); let mut docdef = DocDef::new(doc_name.clone()); let field_name = Name::english("id"); docdef.add_field(field_name.clone(), FieldType::Uuid); docdef.set_default(&field_name, FieldType::Uuid); let mut test_doc: TestDocument = docdef.into(); test_doc.start(standard_paths()); let queue = test_doc.get_queue(); let rx = test_doc.get_receiver(); let mut new_doc = Addition::new(); new_doc.add_field(&field_name, Uuid::nil()); let msg = Message::new(doc_name, new_doc); queue.send(msg).unwrap(); let result = rx.recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(docs) => { assert_eq!(docs.len(), 1); for doc in docs.iter() { let expected: Field = Uuid::nil().into(); assert_eq!(doc.get(&field_name).unwrap(), expected); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } } #[test] fn empty_update_query_results_in_zero_changes() { let count = 5; let mut ids: HashSet = HashSet::new(); while ids.len() < count { ids.insert(Uuid::new_v4()); } let id = ids.iter().last().unwrap().clone(); ids.remove(&id); let mut doc = TestDocument::new([FieldType::Uuid].to_vec()); doc.start(standard_paths()); for id in ids.iter() { doc.populate([id.clone().into()].to_vec()); } let mut calc = Calculation::new(Operand::Equal); calc.add_value(CalcValue::Existing(FieldType::Uuid)) .unwrap(); calc.add_value(Uuid::nil()).unwrap(); let mut query = Query::new(); query.add(Name::english("field0"), calc); let mut update = Update::new(query); update .get_values_mut() .add_field(Name::english("field0"), Uuid::nil()); doc.send(update).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(docs) => assert_eq!(docs.len(), 0), _ => unreachable!("got {:?}: should have gotten a reply", action), } } #[test] fn changes_information_requested() { let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); doc.start(standard_paths()); let doc_name = doc.get_docdef().get_document_names()[0].clone(); let old = "old"; let new = "new"; let id = Uuid::new_v4(); doc.populate([id.into(), old.into()].to_vec()); let mut calc = Calculation::new(Operand::Equal); calc.add_value(CalcValue::Existing(FieldType::Uuid)) .unwrap(); calc.add_value(id.clone()).unwrap(); let mut query = Query::new(); query.add(Name::english("field0"), calc); let mut update = Update::new(query); update .get_values_mut() .add_field(Name::english("field1"), new); let mut testing = |msg: Message| { doc.get_queue().send(msg.clone()).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); let action = result.get_action(); match action { MsgAction::Records(docs) => { assert_eq!(docs.len(), 1, "for {:?}, should have one entry", msg); for doc in docs.iter() { assert_eq!(doc.get(Name::english("field0")).unwrap(), id.into()); assert_eq!(doc.get(Name::english("field1")).unwrap(), new.into()); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } }; testing(Message::new(doc_name.clone(), update)); testing(Message::new(doc_name.clone(), Query::new())); } #[test] fn changes_only_the_queried() { let mut doc = TestDocument::new([FieldType::Integer, FieldType::StaticString].to_vec()); doc.start(standard_paths()); let doc_name = doc.get_docdef().get_document_names()[0].clone(); let old = "old"; let new = "new"; let count = 5; let field_count = count.clone().try_into().unwrap(); let picked = 3; for i in 0..field_count { doc.populate([i.into(), old.into()].to_vec()); } let mut calc = Calculation::new(Operand::Equal); calc.add_value(CalcValue::Existing(FieldType::Integer)) .unwrap(); calc.add_value(picked.clone()).unwrap(); let mut query = Query::new(); query.add(Name::english("field0"), calc); let mut update = Update::new(query); update .get_values_mut() .add_field(Name::english("field1"), new); doc.get_queue() .send(Message::new(doc_name.clone(), update)) .unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(docs) => { assert_eq!(docs.len(), 1, "should have one entry"); for doc in docs.iter() { assert_eq!(doc.get(Name::english("field0")).unwrap(), picked.into()); assert_eq!(doc.get(Name::english("field1")).unwrap(), new.into()); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } doc.get_queue() .send(Message::new(doc_name.clone(), Query::new())) .unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(docs) => { assert_eq!(docs.len(), count, "should have one entry"); for doc in docs.iter() { if doc.get(Name::english("field0")).unwrap() == picked.into() { assert_eq!( doc.get(Name::english("field1")).unwrap(), new.into(), "{:?}", docs ); } else { assert_eq!( doc.get(Name::english("field1")).unwrap(), old.into(), "{:?}", docs ); } } } _ => unreachable!("got {:?}: should have gotten a reply", action), } } #[test] fn can_handle_multiple_updates() { let mut doc = TestDocument::new([FieldType::Integer, FieldType::StaticString].to_vec()); doc.start(standard_paths()); let doc_name = doc.get_docdef().get_document_names()[0].clone(); let old = "old"; let new = "new"; let count = 5; let picked = 3; for i in 0..count { doc.populate([picked.into(), old.into()].to_vec()); } let mut calc = Calculation::new(Operand::Equal); calc.add_value(CalcValue::Existing(FieldType::Integer)) .unwrap(); calc.add_value(picked.clone()).unwrap(); let mut query = Query::new(); query.add(Name::english("field0"), calc); let mut update = Update::new(query); update .get_values_mut() .add_field(Name::english("field1"), new); let mut testing = |msg: Message| { doc.get_queue().send(msg).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(docs) => { assert_eq!(docs.len(), count, "should have one entry"); for doc in docs.iter() { assert_eq!(doc.get(Name::english("field0")).unwrap(), picked.into()); assert_eq!(doc.get(Name::english("field1")).unwrap(), new.into()); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } }; testing(Message::new(doc_name.clone(), update)); testing(Message::new(doc_name.clone(), Query::new())); } #[test] fn update_errors_on_bad_field_name() { let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); doc.start(standard_paths()); let id = Uuid::new_v4(); let old = "old"; let new = "new"; let bad_name = Name::english("wrong"); doc.populate([id.into(), old.into()].to_vec()); let mut update = Update::new(Query::new()); update.get_values_mut().add_field(bad_name.clone(), new); doc.send(update).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(err) => match err { MTTError::NameNotFound(data) => assert_eq!(data, &bad_name), _ => unreachable!("got {:?}: should have gotten an missing field", err), }, _ => unreachable!("got {:?}: should have gotten an error", action), } } #[test] fn update_errors_on_bad_field_type() { let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); doc.start(standard_paths()); let id = Uuid::new_v4(); let old = "old"; let new = Uuid::nil(); doc.populate([id.into(), old.into()].to_vec()); let mut calc = Calculation::new(Operand::Equal); calc.add_value(CalcValue::Existing(FieldType::Uuid)) .unwrap(); calc.add_value(id.clone()).unwrap(); let mut query = Query::new(); query.add(Name::english("field0"), calc); let mut update = Update::new(query); update .get_values_mut() .add_field(Name::english("field1"), new); doc.send(update).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(err) => match err { MTTError::DocumentFieldWrongDataType(expected, got) => { assert_eq!(expected, &FieldType::StaticString); assert_eq!(got, &FieldType::Uuid); } _ => unreachable!("got {:?}: should have gotten incorrect file type", err), }, _ => unreachable!("got {:?}: should have gotten an error", action), } } #[test] fn does_update_maintain_unique_fields() { let mut test_doc = TestDocument::new([FieldType::Integer].to_vec()); test_doc .get_docdef_mut() .add_index(&Name::english("field0"), IndexType::Unique); test_doc.start(standard_paths()); let fname = Name::english("field0"); let old = 3; let new = 5; test_doc.populate([old.into()].to_vec()); let mut calc = Calculation::new(Operand::Equal); calc.add_value(CalcValue::Existing(FieldType::Integer)) .unwrap(); calc.add_value(old.clone()).unwrap(); let mut query = Query::new(); query.add(Name::english("field0"), calc); let mut update = Update::new(query); update.get_values_mut().add_field(&fname, new); test_doc.send(update).unwrap(); test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let mut should_clear = Addition::new(); should_clear.add_field(fname.clone(), old); let mut should_error = Addition::new(); should_error.add_field(fname.clone(), new); test_doc.send(should_clear).unwrap(); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(docs) => { assert_eq!(docs.len(), 1, "should have one entry"); for doc in docs.iter() { assert_eq!(doc.get(&fname).unwrap(), old.into()); } } _ => unreachable!("got {:?}: should have gotten records", action), } test_doc.send(should_error).unwrap(); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(err) => match err { MTTError::FieldDuplicate => {} _ => unreachable!("got {:?}: should have gotten incorrect file type", err), }, _ => unreachable!("got {:?}: should have gotten an error", action), } } #[test] fn unique_value_remains_available_if_failure_occurs() { let f0name = Name::english("field0"); let f1name = Name::english("field1"); let mut test_doc = TestDocument::new([FieldType::Uuid, FieldType::Uuid].to_vec()); test_doc .get_docdef_mut() .add_index(&f0name, IndexType::Unique); test_doc.start(standard_paths()); let f0data = Uuid::new_v4(); let f1bad_data = "NotUuid"; let f1good_data = Uuid::nil(); let mut bad_addition = Addition::new(); bad_addition.add_field(&f0name, f0data.clone()); bad_addition.add_field(&f1name, f1bad_data); let mut good_addition = Addition::new(); good_addition.add_field(&f0name, f0data.clone()); good_addition.add_field(&f1name, f1good_data.clone()); test_doc.send(bad_addition).unwrap(); test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); test_doc.send(good_addition).unwrap(); let result = test_doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(docs) => { assert_eq!(docs.len(), 1, "should have one entry"); for doc in docs.iter() { assert_eq!(doc.get(&f0name).unwrap(), f0data.into()); assert_eq!(doc.get(&f1name).unwrap(), f1good_data.into()); } } _ => unreachable!("got {:?}: should have gotten records", action), } } #[test] fn updating_unique_updates_index_entries() { let fname = Name::english("field0"); let mut doc = TestDocument::new([FieldType::StaticString].to_vec()); doc.get_docdef_mut().add_index(&fname, IndexType::Unique); doc.start(standard_paths()); let old = "old"; let new = "new"; let fold: Field = old.into(); let fnew: Field = new.into(); doc.populate([old.into()].to_vec()); let mut calc = Calculation::new(Operand::Equal); calc.add_value(CalcValue::Existing(FieldType::StaticString)) .unwrap(); calc.add_value(old.clone()).unwrap(); let mut query = Query::new(); query.add(Name::english("field0"), calc); let mut update = Update::new(query); update.get_values_mut().add_field(fname.clone(), new); doc.send(update).unwrap(); doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let mut old_addition = Addition::new(); old_addition.add_field(&fname, old); doc.send(old_addition).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(data) => { assert_eq!(data.len(), 1); for doc in data.iter() { assert_eq!(doc.get(&fname).unwrap(), fold); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } let mut new_addition = Addition::new(); new_addition.add_field(fname.clone(), new); doc.send(new_addition).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(err) => match err { MTTError::FieldDuplicate => {} _ => unreachable!("got {:?}: should have gotten an missing field", err), }, _ => unreachable!("got {:?}: should have gotten an error", action), } } #[test] fn update_does_not_override_unique_index() { let f0name = Name::english("field0"); let f1name = Name::english("field1"); let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); doc.get_docdef_mut().add_index(&f0name, IndexType::Unique); doc.start(standard_paths()); let count = 5; let data = "data"; let mut ids: HashSet = HashSet::new(); while ids.len() < count { ids.insert(Uuid::new_v4()); } let holder = ids.iter().last().unwrap().clone(); let fholder: Field = holder.into(); ids.remove(&holder); for id in ids.iter() { doc.populate([id.clone().into(), data.into()].to_vec()); } let mut calc = Calculation::new(Operand::Equal); calc.add_value(CalcValue::Existing(FieldType::StaticString)) .unwrap(); calc.add_value(data).unwrap(); let mut query = Query::new(); query.add(&f1name, calc); let mut update = Update::new(query); update.get_values_mut().add_field(&f0name, holder.clone()); doc.send(update).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(err) => match err { MTTError::FieldDuplicate => {} _ => unreachable!("got {:?}: should have gotten field duplicate", err), }, _ => unreachable!("got {:?}: should have gotten an error", action), } let query = Query::new(); doc.send(query).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(data) => { assert_eq!(data.len(), ids.len()); for doc in data.iter() { match doc.get(&f0name).unwrap() { Field::Uuid(id) => { assert!(ids.contains(&id)); ids.remove(&id); } _ => unreachable!("did not get uuid"), } } } _ => unreachable!("got {:?}: should have gotten reply", action), } assert!(ids.is_empty(), "did not find {:?}", ids); } #[test] fn can_calculate_field_values() { let fname = Name::english("field0"); let mut doc = TestDocument::new([FieldType::DateTime].to_vec()); doc.start(standard_paths()); let duration = Duration::from_secs(300); let mut calc = Calculation::new(Operand::Add); calc.add_value(FieldType::DateTime).unwrap(); calc.add_value(duration.clone()).unwrap(); let mut addition = Addition::new(); addition.add_field(&fname, calc); let start = Utc::now() + duration; doc.send(addition).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let stop = Utc::now() + duration; let action = result.get_action(); match action { MsgAction::Records(data) => { assert_eq!(data.len(), 1); for doc in data.iter() { match doc.get(&fname).unwrap() { Field::DateTime(datetime) => assert!(datetime > start && datetime < stop), _ => unreachable!("did not get uuid"), } } } _ => unreachable!("got {:?}: should have gotten reply", action), } } #[test] fn can_delete() { let fname = Name::english("field0"); let mut doc = TestDocument::new([FieldType::Integer].to_vec()); doc.start(standard_paths()); doc.populate([1.into()].to_vec()); let mut calc = Calculation::new(Operand::Equal); calc.add_value(1); calc.add_value(CalcValue::Existing(FieldType::Integer)); let mut query = Query::new(); query.add(&fname, calc); let mut delete = Delete::new(query.clone()); doc.send(delete).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(data) => { assert_eq!(data.len(), 1); for doc in data.iter() { match doc.get(&fname).unwrap() { Field::Integer(num) => assert_eq!(num, 1), _ => unreachable!("did not get uuid"), } } } _ => unreachable!("got {:?}: should have gotten reply", action), } doc.send(query).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(data) => assert_eq!(data.len(), 0), _ => unreachable!("got {:?}, should have been empty", action), } } #[test] fn does_delete_return_query_errors() { let field_name = Name::english("wrong"); let mut doc = TestDocument::new([FieldType::Integer].to_vec()); doc.start(standard_paths()); let mut calc = Calculation::new(Operand::Equal); calc.add_value(CalcValue::Existing(FieldType::Integer)); calc.add_value(1); let mut query = Query::new(); query.add(field_name.clone(), calc); let delete = Delete::new(query); doc.send(delete).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(err) => match err { MTTError::NameNotFound(data) => assert_eq!(data, &field_name), _ => unreachable!("got {:?}: should have gotten an missing field", err), }, _ => unreachable!("got {:?}: should have gotten an error", action), } } #[test] fn does_delete_update_indexes() { let fname = Name::english("field0"); let value = 1; let mut doc = TestDocument::new([FieldType::Integer].to_vec()); doc.get_docdef_mut().add_index(&fname, IndexType::Unique); doc.start(standard_paths()); doc.populate([value.into()].to_vec()); doc.send(Delete::new(Query::new())); doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let mut addition = Addition::new(); addition.add_field(&fname, value.clone()); doc.send(addition).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(data) => assert_eq!(data.len(), 1), _ => unreachable!("got {:?}, should have added entry", action), } } #[test] fn can_query_trigger_reaction() { let mut doc = TestDocument::new([FieldType::Integer].to_vec()); let doc_name = doc.get_docdef().get_document_names()[0].clone(); let path = Path::new( Include::All, Include::Some(doc_name.clone().into()), Include::Some(Action::OnQuery), ); let mut update = Update::new(Query::new()); let mut calc = Calculation::new(Operand::Add); calc.add_value(CalcValue::Existing(FieldType::Integer)); calc.add_value(1); update .get_values_mut() .add_field(Name::english("field0"), calc); let function = DocFuncType::ExistingQuery(update.into()); doc.get_docdef_mut().add_route(path, function); let mut paths = standard_paths(); paths.push(Path::new( Include::All, Include::Some(doc_name.into()), Include::Some(Action::OnUpdate), )); doc.start(paths); doc.populate([0.into()].to_vec()); for i in 0..5 { let expected: Field = i.try_into().unwrap(); doc.send(Query::new()).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Records(data) => { assert_eq!(data.len(), 1); for rec in data.iter() { assert_eq!(rec.get(&Name::english("field0")).unwrap(), expected); } } _ => unreachable!("got {:?}, should have added entry", action), } let on_update = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); match on_update.get_action() { MsgAction::OnUpdate(recs) => { let expected: Field = (i + 1).into(); assert_eq!(recs.len(), 1); for rec in recs.iter() { assert_eq!(rec.get(Name::english("field0")).unwrap(), expected); } } _ => unreachable!("should only be on update"), } } } } #[cfg(test)] mod createdocs { use super::support_test::TIMEOUT; use super::*; struct TestCreateDoc { queue: Queue, rx: Receiver, rx_id: Uuid, } impl TestCreateDoc { fn new() -> Self { let mut queue = Queue::new(); let (tx, rx) = channel(); let id = queue.add_sender(tx); CreateDoc::start(queue.clone()); Self { queue: queue, rx: rx, rx_id: id, } } fn get_queue(&self) -> Queue { self.queue.clone() } fn get_receiver(&self) -> &Receiver { &self.rx } fn get_document_id(&self, name: &Name) -> Uuid { let reg_request = Register::new(self.rx_id.clone(), RegMsg::GetNameID(name.clone())); self.queue .send(Message::new(NameType::None, reg_request)) .unwrap(); let info = self.rx.recv_timeout(TIMEOUT).unwrap(); match info.get_action() { MsgAction::Register(data) => match data.get_msg() { RegMsg::DocumentNameID(ident) => ident.clone(), _ => unreachable!("should not get here"), }, _ => unreachable!("should not get here"), } } fn register_paths(&self, paths: Vec) { for path in paths.iter() { let regmsg = Register::new(self.rx_id.clone(), RegMsg::AddRoute(path.clone())); self.queue.send(Message::new(NameType::None, regmsg)); self.rx.recv_timeout(TIMEOUT).unwrap(); } } } #[test] fn create_document_creation() { let doc_creator = TestCreateDoc::new(); let paths = [ Path::new(Include::All, Include::All, Include::Some(Action::Reply)), Path::new(Include::All, Include::All, Include::Some(Action::Records)), ] .to_vec(); doc_creator.register_paths(paths); let mut queue = doc_creator.get_queue(); let rx = doc_creator.get_receiver(); let name = Name::english("project"); let msg1 = Message::new(NameType::None, MsgAction::Create(DocDef::new(name.clone()))); queue.send(msg1.clone()).unwrap(); let result1 = rx.recv_timeout(TIMEOUT).unwrap(); assert_eq!( result1.get_message_id(), msg1.get_message_id(), "received {:?} from create message.", rx ); match result1.get_action() { MsgAction::Reply(_) => {} _ => unreachable!("got {:?}: should have been a reply.", result1.get_action()), } let doc_id: NameType = doc_creator.get_document_id(&name).into(); let msg2 = Message::new(name, Query::new()); queue.send(msg2.clone()).unwrap(); let result2 = rx.recv_timeout(TIMEOUT).unwrap(); assert_eq!(result2.get_message_id(), msg2.get_message_id()); assert_eq!(result2.get_document_id(), &doc_id); match result2.get_action() { MsgAction::Records(data) => assert_eq!(data.len(), 0), _ => unreachable!("got {:?}: should have been a reply.", result1.get_action()), } } #[test] fn does_duplicates_generate_error() { let doc_creator = TestCreateDoc::new(); let paths = [Path::new( Include::All, Include::All, Include::Some(Action::Error), )] .to_vec(); doc_creator.register_paths(paths); let mut queue = doc_creator.get_queue(); let rx = doc_creator.get_receiver(); let name = Name::english("duplicate"); let msg1 = Message::new(NameType::None, MsgAction::Create(DocDef::new(name.clone()))); let msg2 = Message::new(NameType::None, MsgAction::Create(DocDef::new(name.clone()))); queue.send(msg1.clone()).unwrap(); queue.send(msg2.clone()).unwrap(); let result = rx.recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg2.get_message_id()); match result.get_action() { MsgAction::Error(err) => match err { MTTError::NameDuplicate(data) => assert_eq!(data, &name), _ => unreachable!("got {:?}: should have been a duplicate name", err), }, _ => unreachable!("got {:?}: should have been a reply.", result.get_action()), } let mut router = queue.router.read().unwrap(); assert_eq!( router.senders.len(), 3, "there should only be 3 registered senders: createdoc, testing rx, and {:?}", name ); } } struct Clock { queue: Queue, } impl Clock { fn new(queue: Queue) -> Self { Self { queue: queue } } fn start(mut queue: Queue) { let clock = Clock::new(queue.clone()); let (tx, rx) = channel(); let id = queue.add_sender(tx); let reg_msg = Register::new(id, RegMsg::AddDocName([Name::english("clock")].to_vec())); let msg = Message::new(NameType::None, reg_msg.clone()); queue.send(msg).unwrap(); rx.recv().unwrap(); spawn(move || { clock.listen(); }); } fn listen(&self) { loop { self.queue.send(Message::new( Name::english("clock"), Records::new(Names::new()), )); sleep(Duration::from_secs(1)); } } } #[cfg(test)] mod clocks { use super::*; use chrono::TimeDelta; static TIMEOUT: Duration = Duration::from_millis(1500); #[test] fn does_clock_send_reply_every_second() { let mut queue = Queue::new(); let (tx, rx) = channel(); let id = queue.add_sender(tx); let request = Register::new( id.clone(), RegMsg::AddRoute(Path::new(Include::All, Include::All, Include::All)), ); queue.send(Message::new(NameType::None, request)).unwrap(); rx.recv_timeout(TIMEOUT).unwrap(); let mut holder: Vec = Vec::new(); let start = Utc::now(); Clock::start(queue.clone()); while holder.len() < 2 { holder.push(rx.recv_timeout(TIMEOUT).unwrap()); } let end = Utc::now(); assert!((end - start) > TimeDelta::seconds(1)); assert!((end - start) < TimeDelta::seconds(2)); let reg_request = Register::new(id, RegMsg::GetNameID(Name::english("clock"))); queue .send(Message::new(NameType::None, reg_request)) .unwrap(); let info = rx.recv_timeout(TIMEOUT).unwrap(); let doc_id = match info.get_action() { MsgAction::Register(data) => match data.get_msg() { RegMsg::DocumentNameID(ident) => ident.clone(), _ => unreachable!("should not get here"), }, _ => unreachable!("should not get here"), }; for msg in holder.iter() { let name_id = msg.get_document_id(); match name_id { NameType::ID(data) => assert_eq!(data, &doc_id), _ => unreachable!("got {:?}, should have been clock", name_id), } let action = msg.get_action(); match action { MsgAction::Records(result) => assert_eq!(result.len(), 0), _ => unreachable!("got {:?}, should have been empty record", action), } } } } #[derive(Clone, Debug)] struct MsgEntry { timestamp: DateTime, message: Message, } impl MsgEntry { fn new(msg: Message) -> Self { Self { timestamp: Utc::now(), message: msg, } } fn get_timestamp(&self) -> &DateTime { &self.timestamp } fn get_message(&self) -> &Message { &self.message } fn get_message_id(&self) -> &Uuid { self.message.get_message_id() } } #[cfg(test)] mod msg_entries { use super::*; #[test] fn creates_message_entry() { let msg = Message::new(Name::english("holder"), Query::new()); let start = Utc::now(); let entry = MsgEntry::new(msg.clone()); let end = Utc::now(); assert!( entry.get_timestamp() > &start, "timestamp should be between start and end times" ); assert!( entry.get_timestamp() < &end, "timestamp should be between start and end times" ); assert_eq!(entry.get_message_id(), msg.get_message_id()); } } #[derive(Clone, Debug)] struct MsgLogs { data: HashMap>, } impl MsgLogs { fn new() -> Self { Self { data: HashMap::new(), } } fn add(&mut self, msg: Message) { let entry = MsgEntry::new(msg); let id = entry.get_message_id(); let entries = match self.data.get_mut(id) { Some(data) => data, None => { self.data.insert(id.clone(), Vec::new()); self.data.get_mut(id).unwrap() } }; entries.push(entry); } fn get(&self, msg_id: &Uuid) -> Option<&Vec> { self.data.get(msg_id) } } #[cfg(test)] mod msg_logs { use super::*; #[test] fn can_add_message_to_log() { let mut logs = MsgLogs::new(); let msg = Message::new(Name::english("something"), Query::new()); logs.add(msg.clone()); let result = logs.get(msg.get_message_id()).unwrap(); assert_eq!(result.len(), 1, "should be one entry"); } #[test] fn returns_none_when_no_logs_found() { let logs = MsgLogs::new(); match logs.get(&Uuid::nil()) { Some(data) => unreachable!("got {:?}, should return none", data), None => (), } } #[test] fn stores_messages_with_responses() { let mut logs = MsgLogs::new(); let msg1 = Message::new(Name::english("something"), Query::new()); let msg2 = msg1.response(Records::new(Names::new())); logs.add(msg1.clone()); logs.add(msg2.clone()); let result = logs.get(msg1.get_message_id()).unwrap(); assert_eq!(result.len(), 2, "should be two entry"); let action1: Action = result[0].get_message().get_action().clone().into(); let action2: Action = result[1].get_message().get_action().clone().into(); assert_eq!(action1, Action::Query); assert_eq!(action2, Action::Records); } #[test] fn messages_are_stored_by_ids() { let mut logs = MsgLogs::new(); let msg1 = Message::new(Name::english("something"), Query::new()); let msg2 = Message::new(Name::english("something"), Query::new()); logs.add(msg1.clone()); logs.add(msg2.clone()); let result1 = logs.get(msg1.get_message_id()).unwrap(); let result2 = logs.get(msg2.get_message_id()).unwrap(); assert_eq!(result1.len(), 1, "should be one entry"); assert_eq!(result2.len(), 1, "should be one entry"); assert_eq!(result1[0].get_message_id(), msg1.get_message_id()); assert_eq!(result2[0].get_message_id(), msg2.get_message_id()); } } struct MessageLog { data: MsgLogs, queue: Queue, rx: Receiver, } impl MessageLog { fn new(queue: Queue, rx: Receiver) -> Self { Self { data: MsgLogs::new(), queue: queue, rx: rx, } } fn start(mut queue: Queue) { let (tx, rx) = channel(); let mut logs = MessageLog::new(queue.clone(), rx); let id = queue.add_sender(tx); let reg_msg = Register::new( id, RegMsg::AddRoute(Path::new(Include::All, Include::All, Include::All)), ); let rmsg = Message::new(NameType::None, reg_msg); queue.send(rmsg.clone()).unwrap(); spawn(move || { logs.listen(); }); } fn listen(&mut self) { loop { let msg = self.rx.recv().unwrap(); match msg.get_action() { MsgAction::GetLog(id) => match self.data.get(id) { Some(data) => self .queue .send(msg.response(MsgAction::Log(data.clone()))) .unwrap(), None => self .queue .send(msg.response(MsgAction::Log(Vec::new()))) .unwrap(), }, _ => self.data.add(msg), } } } } #[cfg(test)] mod message_logs { use super::{support_test::TIMEOUT, *}; #[test] fn does_log_store_messages() { let doc_name = Name::english("unimportant"); let mut queue = Queue::new(); MessageLog::start(queue.clone()); let (tx, rx) = channel(); let id = queue.add_sender(tx); let reg_msg = Register::new(id, RegMsg::AddDocName(vec![doc_name.clone()])); let rmsg = Message::new(NameType::None, reg_msg); queue.send(rmsg.clone()).unwrap(); let name_result = rx.recv().unwrap(); let name_id = match name_result.get_action() { MsgAction::Register(data) => match data.get_msg() { RegMsg::DocumentNameID(data) => data, RegMsg::Error(err) => unreachable!("got {:?}, should have gotten data", err), _ => unreachable!("should only return a name id or an error"), }, _ => unreachable!("should only return a name id or an error"), }; let request = Register::new( id.clone(), RegMsg::AddRoute(Path::new( Include::All, Include::All, Include::Some(Action::Log), )), ); queue.send(Message::new(NameType::None, request)).unwrap(); rx.recv_timeout(TIMEOUT).unwrap(); let msg = Message::new(doc_name.clone(), Query::new()); let start = Utc::now(); queue.send(msg.clone()).unwrap(); let log_msg = Message::new(NameType::None, msg.get_message_id()); queue.send(log_msg.clone()).unwrap(); let result = rx.recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), log_msg.get_message_id()); match result.get_action() { MsgAction::Log(output) => assert_eq!(output.len(), 1), _ => unreachable!("got {:?}, should have been log", result.get_action()), } } }