use chrono::prelude::*; use isolang::Language; use std::{ collections::{HashMap, HashSet, VecDeque}, sync::{ mpsc::{channel, Receiver, Sender}, Arc, RwLock, }, thread::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), DocumentAlreadyExists(String), DocumentFieldAlreadyExists(String, Field), DocumentFieldMissing(String), DocumentFieldNotFound(String), DocumentFieldWrongDataType(FieldType, FieldType), DocumentNotFound(String), FieldDuplicate(String, Field), FieldMissingData, InvalidNone, RecordMismatch, NameDuplicate(Name), NameInvalidID(Uuid), NameMissingTranslation(Language), NameNotFound(Name), QueryCannotChangeData, } #[derive(Clone, Debug, Eq, Hash, PartialEq)] enum Action { Addition, Create, Delete, Error, 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::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), Query(Query), Records(RecordIter), 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: RecordIter) -> 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) } } #[cfg(test)] mod msgactions { use super::*; #[test] fn turn_document_definition_into_action() { let name = Name::english(Uuid::new_v4().to_string()); let value = DocDef::new(name.clone()); let result: MsgAction = value.into(); match result { MsgAction::Create(def) => assert_eq!(def.get_document_name(), &name), _ => 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 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".to_string()), Name::english("two".to_string()), ]; 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(), MsgAction::Query(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".to_string()), 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".to_string()); let msg = Message::new(name.clone(), MsgAction::Query(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".to_string()); let msg = Message::new(name.clone(), MsgAction::Query(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(), MsgAction::Query(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), } } } #[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, 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()), }, } } } #[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 get_language(&self) -> &Language { &self.lang } fn english(name: String) -> Self { Self { name: name, lang: Language::from_639_1("en").unwrap(), } } fn japanese(name: String) -> Self { Self { name: name, lang: Language::from_639_1("ja").unwrap(), } } } impl ToString for Name { 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_name(&mut self, name: Name) -> Result { if self.names.contains_key(&name) { return Err(MTTError::NameDuplicate(name)); } let mut id = Uuid::new_v4(); while self.ids.contains_key(&id) { id = Uuid::new_v4(); } self.names.insert(name.clone(), id.clone()); let mut holder: HashMap = HashMap::new(); holder.insert(name.get_language().clone(), name); self.ids.insert(id.clone(), holder); Ok(id) } fn add_translation(&mut self, name: Name, translation: Name) -> Result { let id = match self.get_id(&name) { Ok(data) => data.clone(), Err(err) => return Err(err), }; match self.get_id(&translation) { Ok(_) => return Err(MTTError::NameDuplicate(translation)), Err(_) => {} } let holder = self.ids.get_mut(&id).unwrap(); holder.insert(translation.get_language().clone(), translation.clone()); self.names.insert(translation, id); Ok(id.clone()) } fn get_name(&self, id: &Uuid, lang: &Language) -> Result { match self.ids.get(id) { Some(langdb) => match langdb.get(lang) { Some(name) => Ok(name.clone()), None => Err(MTTError::NameMissingTranslation(lang.clone())), }, None => Err(MTTError::NameInvalidID(id.clone())), } } fn get_id(&self, name: 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 { Err(MTTError::NameNotFound(Name::english(data.to_string()))) } } NameType::None => Err(MTTError::NameNotFound(Name::english("none".to_string()))), } } 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_name_ids_unique() { let mut names = Names::new(); let data = ["one", "two", "three", "four", "five"]; let mut ids: HashSet = HashSet::new(); for item in data.iter() { let name = Name::english(item.to_string()); ids.insert(names.add_name(name).unwrap()); } assert_eq!(ids.len(), data.len()); } #[test] fn does_id_return_name() { let mut names = Names::new(); let data = ["one", "two"]; let mut ids: HashMap = HashMap::new(); for item in data.iter() { let name = Name::english(item.to_string()); ids.insert(name.clone(), names.add_name(name).unwrap()); } for (name, id) in ids.iter() { assert_eq!( &names .get_name(id, &Language::from_639_1("en").unwrap()) .unwrap(), name ); assert_eq!(&names.get_id(name).unwrap(), id); } } #[test] fn errors_on_name_not_found() { let mut names = Names::new(); let name = Name::english("missing".to_string()); let result = names.get_id(&name); match result { Ok(_) => unreachable!("got {:?}, should have been error", result), Err(err) => match err { MTTError::NameNotFound(output) => assert_eq!(output, name), _ => unreachable!("got {:?}, should have been name not found", err), }, } } #[test] fn errors_on_bad_id() { let mut names = Names::new(); let id = Uuid::new_v4(); let result = names.get_name(&id, &Language::from_639_1("en").unwrap()); match result { Ok(_) => unreachable!("got {:?}, should be invalid id error", result), Err(err) => match err { MTTError::NameInvalidID(data) => assert_eq!(data, id), _ => unreachable!("got {:?}, should have been invalid id", err), }, } } #[test] fn errors_on_missing_translation() { let mut names = Names::new(); let name = Name::english("task".to_string()); let lang = Language::from_639_1("ja").unwrap(); let id = names.add_name(name).unwrap(); let result = names.get_name(&id, &lang); match result { Ok(_) => unreachable!("got {:?}, should be invalid id error", result), Err(err) => match err { MTTError::NameMissingTranslation(data) => assert_eq!(data, lang), _ => unreachable!("got {:?}, should have been invalid id", err), }, } } #[test] fn errors_on_duplicate_names() { let mut names = Names::new(); let data = "test".to_string(); let name = Name::english(data.clone()); let id = names.add_name(name.clone()); let output = names.add_name(name.clone()); match output { Ok(_) => unreachable!( "got {:?}, should have produced duplicate name error", output ), Err(err) => match err { MTTError::NameDuplicate(result) => assert_eq!(result, name), _ => unreachable!("got {:?}, should have been duplicate name", err), }, } } #[test] fn allows_alternate_names() { let mut names = Names::new(); let data = "test".to_string(); let alt = "テスト".to_string(); let english = Name::english(data.clone()); let japanese = Name::japanese(alt.clone()); let id = names.add_name(english.clone()).unwrap(); let result = names.add_translation(english, japanese.clone()).unwrap(); assert_eq!(result, id); let output = names.get_name(&id, &Language::from_639_1("ja").unwrap()); assert_eq!(output.unwrap().to_string(), alt); assert_eq!(names.get_id(&japanese).unwrap(), id); } #[test] fn errors_on_bad_translation() { let mut names = Names::new(); let data = "test".to_string(); let alt = "テスト".to_string(); let english = Name::english(data.clone()); let japanese = Name::japanese(alt.clone()); let result = names.add_translation(japanese.clone(), english); match result { Ok(_) => unreachable!("got {:?}, should be invalid id error", result), Err(err) => match err { MTTError::NameNotFound(output) => assert_eq!(output, japanese), _ => unreachable!("got {:?}, should have been invalid id", err), }, } } #[test] fn errors_on_translation_duplicates() { let mut names = Names::new(); let data = "test".to_string(); let alt = "テスト".to_string(); let english = Name::english(data.clone()); let japanese = Name::japanese(alt.clone()); let id = names.add_name(english.clone()).unwrap(); let id = names.add_name(japanese.clone()).unwrap(); let result = names.add_translation(english, japanese.clone()); match result { Ok(_) => unreachable!( "got {:?}, should have produced duplicate name error", result ), Err(err) => match err { MTTError::NameDuplicate(result) => assert_eq!(result, japanese), _ => unreachable!("got {:?}, should have been duplicate name", err), }, } } #[test] fn convert_path_to_route_with_ids() { let mut names = Names::new(); let data = "data".to_string(); let english = Name::english(data.clone()); let id = names.add_name(english.clone()).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".to_string(); let english = Name::english(data.clone()); let id = names.add_name(english.clone()).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".to_string()); 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(Name), Error(MTTError), Ok, } #[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, }, } } } #[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 QueueData { senders: HashMap>, names: HashMap, routes: HashMap>, } impl QueueData { fn new() -> Self { Self { senders: HashMap::new(), names: HashMap::new(), routes: HashMap::new(), } } fn get_doc_id(&self, nameid: N) -> Result where N: Into, { let sender_id = match nameid.into() { NameID::Name(name) => match self.names.get(&name) { Some(id) => id.clone(), None => return Err(MTTError::DocumentNotFound(name.clone())), }, NameID::ID(id) => id.clone(), NameID::None => unreachable!("should never be none"), }; if self.senders.contains_key(&sender_id) { Ok(sender_id) } else { Err(MTTError::DocumentNotFound(sender_id.to_string())) } } fn register( &mut self, tx: Sender, name: String, routes: Vec, ) -> Result<(), MTTError> { let mut id = Uuid::new_v4(); while self.senders.contains_key(&id) { id = Uuid::new_v4(); } match self.get_doc_id(name.clone()) { Ok(_) => return Err(MTTError::DocumentAlreadyExists(name)), Err(_) => {} } let mut holder: HashMap> = HashMap::new(); for route in routes.iter() { let doc_type = match &route.doc_name { Include::Some(doc_name) => { if doc_name == &name { Include::Some(id.clone()) } else { match self.get_doc_id(doc_name.to_string()) { Ok(doc_id) => Include::Some(doc_id), Err(err) => return Err(err), } } } Include::All => Include::All, }; let route_id: RouteID = Route::new(route.msg_id.clone(), doc_type, route.action.clone()).into(); match self.routes.get(&route_id) { Some(senders) => { let mut addition = senders.clone(); addition.push(id.clone()); holder.insert(route_id, addition); } None => { let senders = [id.clone()].to_vec(); holder.insert(route_id, senders); } } } self.senders.insert(id.clone(), tx); self.names.insert(name.clone(), id.clone()); for (route_id, senders) in holder.iter() { self.routes.insert(route_id.clone(), senders.clone()); } Ok(()) } fn send(&self, msg: Message) -> Result<(), MTTError> { let doc_id: Include = match self.get_doc_id(msg.get_document_id()) { Ok(id) => Include::Some(id.clone()), Err(err) => { let action: Action = msg.get_action().into(); if action == Action::Create { Include::Some(Uuid::nil()) } else { return Err(err); } } }; let route = Route::new( Include::Some(msg.get_message_id().clone()), doc_id, Include::Some(msg.get_action().into()), ); for (send_route, send_ids) in self.routes.iter() { if route == send_route.into() { for send_id in send_ids { let tx = self.senders.get(&send_id).unwrap(); tx.send(msg.clone()).unwrap(); } } } Ok(()) } } #[cfg(test)] mod queuedatas { use super::support_test::TIMEOUT; use super::*; use std::sync::mpsc::RecvTimeoutError; #[test] fn can_document_be_registered() { let mut queuedata = QueueData::new(); let (tx, rx) = channel(); let name = Uuid::new_v4().to_string(); let routes = [ RouteRequest::new( Include::All, Include::Some(name.clone()), Include::Some(Action::Query), ), RouteRequest::new( Include::All, Include::Some(name.clone()), Include::Some(Action::Reply), ), ] .to_vec(); queuedata.register(tx, name.clone(), routes).unwrap(); let msg1 = Message::new(name.clone(), MsgAction::Query(Query::new())); let msg2 = Message::new(name.clone(), MsgAction::Reply(Reply::new())); let msg3 = Message::new(name.clone(), MsgAction::Create(DocDef::new())); queuedata.send(msg1.clone()).unwrap(); queuedata.send(msg2.clone()).unwrap(); queuedata.send(msg3.clone()).unwrap(); let result1 = rx.recv_timeout(TIMEOUT).unwrap(); let result2 = rx.recv_timeout(TIMEOUT).unwrap(); match rx.recv_timeout(TIMEOUT) { Ok(_) => unreachable!("should have timed out"), Err(err) => match err { RecvTimeoutError::Timeout => {} _ => unreachable!("should have timed out"), }, } assert_eq!(result1.get_message_id(), msg1.get_message_id()); assert_eq!(result2.get_message_id(), msg2.get_message_id()); match result1.get_action() { MsgAction::Query(_) => {} _ => unreachable!("should have been a query"), } match result2.get_action() { MsgAction::Reply(_) => {} _ => unreachable!("should have been a query"), } } #[test] fn does_register_fail_on_duplicate_documents() { let mut queuedata = QueueData::new(); let (tx1, _) = channel(); let (tx2, _) = channel(); let name = Uuid::new_v4().to_string(); queuedata .register(tx1, name.to_string(), Vec::new()) .unwrap(); match queuedata.register(tx2, name.to_string(), Vec::new()) { Ok(_) => unreachable!("duplicates should create an error"), Err(err) => match err { MTTError::DocumentAlreadyExists(result) => assert_eq!(result, name), _ => unreachable!("should have been document does not exists"), }, } } #[test] fn does_bad_route_prevent_registration() { let mut queuedata = QueueData::new(); let (tx, rx) = channel(); let good = "good"; let bad = Uuid::new_v4().to_string(); let routes = [ RouteRequest::new( Include::All, Include::Some(good.to_string()), Include::Some(Action::Query), ), RouteRequest::new( Include::All, Include::Some(bad.clone()), Include::Some(Action::Reply), ), ] .to_vec(); match queuedata.register(tx, good.to_string(), routes) { Ok(_) => unreachable!("should produce an error"), Err(err) => match err { MTTError::DocumentNotFound(result) => assert_eq!(result, bad), _ => unreachable!("Shouuld be document not found"), }, } assert_eq!(queuedata.senders.len(), 0, "should not add to senders"); assert_eq!(queuedata.names.len(), 0, "should not add to names"); assert_eq!(queuedata.routes.len(), 0, "should nor add to routes"); } #[test] fn is_sender_only_added_once_to_routes() { let mut queuedata = QueueData::new(); let (tx, rx) = channel(); let name = "project"; let routes = [ RouteRequest::new( Include::All, Include::Some(name.to_string()), Include::Some(Action::Query), ), RouteRequest::new( Include::All, Include::Some(name.to_string()), Include::Some(Action::Query), ), ] .to_vec(); queuedata.register(tx, name.to_string(), routes).unwrap(); for senders in queuedata.routes.values() { assert_eq!(senders.len(), 1, "should be no double entries"); } } #[test] fn does_a_bad_document_name_fail() { let docname = Uuid::new_v4().to_string(); let queuedata = QueueData::new(); let msg = Message::new(docname.clone(), MsgAction::Query(Query::new())); match queuedata.send(msg) { Ok(_) => unreachable!("should have been an error"), Err(data) => match data { MTTError::DocumentNotFound(doc) => assert_eq!(doc, docname), _ => unreachable!("should have been a not found error"), }, } } #[test] fn is_send_okay_if_no_one_is_listening() { let mut queuedata = QueueData::new(); let name = "something"; let (tx, _) = channel(); queuedata .register(tx, name.to_string(), Vec::new()) .unwrap(); let msg = Message::new("something", MsgAction::Create(DocDef::new())); match queuedata.send(msg) { Ok(_) => {} Err(err) => unreachable!("got {:?}: should not error", err), } } #[test] fn can_more_than_one_document_respond() { let mut queuedata = QueueData::new(); let name1 = "task"; let name2 = "work"; let action = MsgAction::Query(Query::new()); let (tx1, rx1) = channel(); let (tx2, rx2) = channel(); let routes = [RouteRequest::new( Include::All, Include::Some(name1.to_string()), Include::All, )] .to_vec(); queuedata .register(tx1, name1.to_string(), routes.clone()) .unwrap(); queuedata .register(tx2, name2.to_string(), routes.clone()) .unwrap(); let msg = Message::new(name1, action.clone()); queuedata.send(msg.clone()).unwrap(); let result1 = rx1.recv_timeout(TIMEOUT).unwrap(); let result2 = rx2.recv_timeout(TIMEOUT).unwrap(); assert_eq!(result1.get_message_id(), msg.get_message_id()); assert_eq!(result1.get_message_id(), result2.get_message_id()); } } */ 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 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); } _ => { let path = msg.get_path(); match self.doc_names.path_to_route(&path) { Ok(route) => { 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(name) => match self.doc_names.add_name(name.clone()) { Ok(_) => reg.response(RegMsg::Ok), 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::Ok) } _ => 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 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".to_string()), 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".to_string()), 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"); } } #[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 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_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_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())); let msg = Message::new(NameType::None, reg_msg); self.queue.send(msg.clone()).unwrap(); let result = rx.recv_timeout(TIMEOUT).unwrap(); self.doc_id.insert(name.clone(), id); self.doc_rx.insert(name.clone(), rx); } fn get_preset_id(&self) -> &Uuid { &self.sender_id } fn get_preset_rx(&self) -> &Receiver { &self.receiver } fn get_doc_rx_id(&self, name: &Name) -> &Uuid { self.doc_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".to_string()), 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"); } #[test] fn can_register_document_name() { let mut tester = TestQueue::new(); let mut queue = tester.get_queue(); let doc_name = Name::english(Uuid::new_v4().to_string()); let reg_msg = Register::new( tester.get_preset_id().clone(), RegMsg::AddDocName(doc_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(); 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::Ok => {} _ => unreachable!("got {:?}, should have been register ok", action), }, _ => unreachable!("got {:?}, should have been register ok", action), } } #[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()); let reg_msg = Register::new( tester.get_preset_id().clone(), RegMsg::AddDocName(doc_name.clone()), ); 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".to_string()), Name::english("recipe".to_string()), ]; 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".to_string()), Name::english("recipe".to_string()), ]; 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".to_string()); 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".to_string()), Name::english("two".to_string()), ]; 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(); tester.get_doc_rx(&name).recv_timeout(TIMEOUT).unwrap(); 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".to_string()), Name::english("two".to_string()), ]; 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(); tester.get_doc_rx(&name).recv_timeout(TIMEOUT).unwrap(); 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".to_string()), Name::english("two".to_string()), ]; 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(); tester.get_doc_rx(&name).recv_timeout(TIMEOUT).unwrap(); 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 = [RouteRequest::new( Include::All, Include::All, Include::Some(Action::Create), )] .to_vec(); //let id = queue.register(tx, "document".to_string(), routes).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, PartialEq)] enum Field { Boolean(bool), DateTime(DateTime), Duration(Duration), Integer(i128), None, StaticString(String), Uuid(Uuid), } impl Field { fn get_type(&self) -> FieldType { self.into() } } 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::*; #[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); } } #[derive(Clone, Debug)] struct FieldSetting { fieldtype: FieldType, default_value: Option, } impl FieldSetting { fn new(ftype: FieldType) -> Self { Self { fieldtype: ftype, default_value: None, } } fn set_default(&mut self, value: Calculation) -> Result<(), MTTError> { let data = value.calculate(); match self.validate(Some(data)) { Ok(_) => {} Err(err) => return Err(err), } self.default_value = Some(value); Ok(()) } fn validate(&self, value: Option) -> Result { match value { Some(data) => { let vft: FieldType = (&data).into(); if vft != self.fieldtype { return Err(MTTError::DocumentFieldWrongDataType( self.fieldtype.clone(), vft, )); } Ok(data.clone()) } None => match &self.default_value { Some(calc) => Ok(calc.calculate()), None => Err(MTTError::InvalidNone), }, } } } #[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(Some(value.clone())) { 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(Some(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(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); let mut calc = Calculation::new(Operand::Assign); calc.add_value(FieldType::StaticString); fset.set_default(calc); match fset.validate(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"; let mut calc = Calculation::new(Operand::Assign); calc.add_value(input); fset.set_default(calc); match fset.validate(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(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) -> Option where NT: Into, { self.data.get_field(name) } fn get_document(&self) -> Document { self.data.clone() } } #[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()); let data = Uuid::new_v4().to_string(); add.add_field(name.clone(), data.clone()); let result = add.get_field(&name).unwrap(); match result { Field::StaticString(result) => assert_eq!(result, data), _ => unreachable!("got {:?}: should have received static string", result), } } fn can_add_uuid() { let mut add = Addition::new(); let name = Name::english(Uuid::new_v4().to_string()); let data = Uuid::new_v4(); add.add_field(name.clone(), data.clone()); let result = add.get_field(&name).unwrap(); match result { Field::Uuid(result) => assert_eq!(result, data), _ => unreachable!("got {:?}: should have received uuid", result), } } fn can_get_document() { let mut add = Addition::new(); let name = Name::english(Uuid::new_v4().to_string()); let data = Uuid::new_v4(); add.add_field(name.clone(), data.clone()); let doc: Document = add.get_document(); match doc.get_field(&name).unwrap() { Field::Uuid(result) => assert_eq!(result, data), _ => unreachable!("should have received uuid"), } } } #[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)] struct DocDef { doc_name: Name, field_names: Names, fields: HashMap, indexes: HashMap, } impl DocDef { fn new(name: Name) -> Self { Self { doc_name: name, field_names: Names::new(), fields: HashMap::new(), indexes: HashMap::new(), } } fn get_document_name(&self) -> &Name { &self.doc_name } 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_name(name).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: Option) -> 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: Calculation) -> Result<(), MTTError> { 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), } /* let setting = match self.get_field_mut(field_name) { Ok(data) => data, Err(err) => return Err(err), }; match setting.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(()) /* let setting = match self.get_field(&field_name) { Ok(_) => {} Err(err) => return Err(err), }; self.indexes.insert(field_name, index_type); Ok(()) */ } fn create_indexes(&self) -> Indexes { Indexes::new(&self.indexes) } fn iter(&self) -> impl Iterator { self.fields.iter() } } #[cfg(test)] mod docdefs { use super::*; #[test] fn can_field_be_added() { let docname = Name::english("tester".to_string()); let mut docdef = DocDef::new(docname); let name = Name::english(Uuid::new_v4().to_string()); let field_type = FieldType::Uuid; docdef.add_field(name.clone(), field_type.clone()); let result = docdef.get_field(name).unwrap(); match result.validate(Some(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".to_string()); let docdef = DocDef::new(docname); let name = Name::english(Uuid::new_v4().to_string()); 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".to_string()); 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.to_string()), field_type.clone()); } for name in names.iter() { let result = docdef.get_field(Name::english(name.to_string())).unwrap(); match result.validate(Some("".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".to_string()); let mut docdef = DocDef::new(docname); let name = Name::english("defaultfunction".to_string()); docdef.add_field(name.clone(), FieldType::StaticString); let mut calc = Calculation::new(Operand::Assign); calc.add_value(FieldType::StaticString); docdef.set_default(&name, calc); match docdef.get_field(name).unwrap().validate(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_function_error_on_bad_field_name() { let docname = Name::english("something".to_string()); let mut docdef = DocDef::new(docname); let field_name = Name::english("wrong".to_string()); let calc = Calculation::new(Operand::Assign); match docdef.set_default(&field_name, calc) { 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_name() { let docname = Name::english("something".to_string()); let mut docdef = DocDef::new(docname); let field_name = Name::english(Uuid::new_v4().to_string()); let mut calc = Calculation::new(Operand::Assign); calc.add_value(Uuid::new_v4()); match docdef.set_default(&field_name, calc) { 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".to_string()); let mut docdef = DocDef::new(docname); let name = Name::english("defaultvalue".to_string()); docdef.add_field(name.clone(), FieldType::Uuid); let mut calc = Calculation::new(Operand::Assign); calc.add_value("fred"); match docdef.set_default(&name, calc) { Ok(data) => unreachable!("got {:?}, should be 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 been field not found", err), }, } } /* #[test] fn returns_field_ids() { let count = 5; let mut ids: HashSet = HashSet::new(); while ids.len() < count { ids.insert(Name::english(Uuid::new_v4().to_string())); } let docname = Name::english("something".to_string()); let mut docdef = DocDef::new(docname); for id in ids.iter() { docdef.add_field(id.clone(), FieldType::Uuid); } let result = docdef.field_ids(); assert_eq!(result.len(), ids.len()); for id in result.iter() { assert!(ids.contains(id.clone())); } } */ } #[derive(Clone, Debug)] enum Operand { Add, Assign, Equal, } impl Operand { fn validate(&self, x: &Field, y: &Field) -> bool { match self { Self::Equal => 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)); } } #[derive(Clone, Debug)] enum CalcValue { Calculate(Calculation), FType(FieldType), Value(Field), } impl CalcValue { fn get(&self) -> Field { match self { Self::FType(ftype) => ftype.get_default(), Self::Value(field) => field.clone(), Self::Calculate(calc) => calc.calculate(), } } } 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 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(), 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(), 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(), 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(), 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(), 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(), 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(), 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::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) -> Vec { let mut output = Vec::new(); for item in self.values.iter() { output.push(item.get()); } output } fn push_value( &mut self, base: FieldType, ftype: FieldType, data: CalcValue, ) -> Result<(), MTTError> { if base == ftype { self.values.push(data); } else { return Err(MTTError::DocumentFieldWrongDataType(base, ftype)); } Ok(()) } fn add_value(&mut self, data: CV) -> Result<(), MTTError> where CV: Into, { let holder: CalcValue = data.into(); if self.values.len() == 0 { self.values.push(holder); Ok(()) } else { let mut base = self.values[0].get().get_type(); let ftype = holder.get().get_type(); match self.operation { Operand::Add => { if base == FieldType::DateTime { base = FieldType::Duration; } match self.push_value(base, ftype, holder) { Ok(_) => Ok(()), Err(err) => Err(err), } } _ => match self.push_value(base, ftype, holder) { Ok(_) => Ok(()), Err(err) => Err(err), }, } } } fn calculate(&self) -> Field { match self.operation { Operand::Add => { let values = self.get_fields(); match values[0].get_type() { FieldType::DateTime => { let mut output = Utc::now(); for item in values.iter() { match item { Field::DateTime(datetime) => output = datetime.clone(), Field::Duration(duration) => output += duration.clone(), _ => unreachable!("got {:?}, should have been a duration", item), } } output.into() } FieldType::Integer => { let mut output: i128 = 0; for item in values.iter() { match item { Field::Integer(data) => output += data, _ => unreachable! {"got {:?} expected Integer", item}, } } output.into() } _ => unreachable!("{:?} does not handle addition", values[0].get_type()), } } Operand::Assign => self.values[0].get(), Operand::Equal => { self.values[0].get() == self.values[1].get() }.into(), } } } #[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::Assign); match calc.operation() { Operand::Assign => {} _ => 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_assign_value() { let mut calc = Calculation::new(Operand::Assign); let data: Field = Uuid::new_v4().into(); calc.add_value(data.clone()); let result = calc.calculate(); assert_eq!(result, data); } #[test] fn can_assign_default_function() { let mut calc = Calculation::new(Operand::Assign); calc.add_value(FieldType::Uuid); let result1 = calc.calculate(); let result2 = calc.calculate(); assert_ne!(result1, result2); } #[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(); 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(); 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); calc.add_value(value2); let result = calc.calculate(); assert_eq!(result, 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(); 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)] 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() //self.data.keys().cloned().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()); 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(), 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(), expected); } None => unreachable!("should have returned a calculation"), } } #[test] fn does_not_allow_data_changes() { let mut calc = Calculation::new(Operand::Assign); calc.add_value(Uuid::nil()); let mut query = Query::new(); match query.add(Name::english("name".to_string()), calc) { Ok(_) => unreachable!("Should have received an error"), Err(err) => match err { MTTError::QueryCannotChangeData => {} _ => unreachable!("got {:?}, should have gotten cannot change data", err), }, } } /* #[test] fn returns_set_of_fields() { let count = 5; let mut field_ids: HashSet = HashSet::new(); while field_ids.len() < count { field_ids.insert(Name::english(Uuid::new_v4().to_string())); } let mut query = Query::new(); for field_id in field_ids.iter() { query.add(field_id.clone(), Calculation::new(Operand::Equal)); } let result = query.field_ids(); assert_eq!(result.len(), field_ids.len()); for field_id in result.iter() { assert!( field_ids.contains(field_id), "field id {:?} not found", field_id ); } } */ } #[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".to_string()); 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).unwrap() { Field::StaticString(output) => assert_eq!(output, "one"), _ => unreachable!("got {:?}: should have been static string", result1), } let result2 = reply_iter.next().unwrap(); match result2.get_field(&fieldname).unwrap() { Field::StaticString(output) => assert_eq!(output, "two"), _ => unreachable!("got {:?}: should have been static string", 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) -> Option where F: Into { self.data.insert(id, data.into()) } 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 Record { names: Names, data: InternalRecord, } impl Record { fn new(names: Names) -> Self { Self { names: names, data: InternalRecord::new(), } } fn with_data(names: Names, rec: InternalRecord) -> Self { Self { names: names, data: rec, } } fn insert(&mut self, field_id: NT, data: F) -> Result<(), MTTError> where F: Into, NT: Into, { let id = match self.names.get_id(field_id) { Ok(data) => data, Err(err) => return Err(err), }; self.data.insert(id, data.into()); Ok(()) } 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 RecordIter { names: Names, records: VecDeque, } impl RecordIter { fn new() -> Self { Self { names: Names::new(), records: VecDeque::new(), } } fn with_names(names: Names) -> Self { Self { names: names, records: VecDeque::new(), } } fn push(&mut self, rec: Record) -> Result<(), MTTError> { if self.names.is_empty() { self.names = rec.names; self.records.push_back(rec.data.clone()); } else { if rec.names != self.names { return Err(MTTError::RecordMismatch); } let existing: HashSet = self.records.front().unwrap().keys().cloned().collect(); let rec_keys: HashSet = rec.data.keys().cloned().collect(); if existing == rec_keys { self.records.push_back(rec.data.clone()); } else { return Err(MTTError::RecordMismatch); } } Ok(()) } fn insert(&mut self, rec: InternalRecord) -> Result<(), MTTError> { self.records.push_back(rec.clone()); Ok(()) } } impl Iterator for RecordIter { type Item = Record; fn next(&mut self) -> Option { match self.records.pop_front() { Some(data) => Some(Record { names: self.names.clone(), data: data, }), None => None, } } } impl ExactSizeIterator for RecordIter { fn len(&self) -> usize { self.records.len() } } #[cfg(test)] mod records { use super::*; #[test] fn can_create_a_record() { let mut names = Names::new(); let count = 5; let mut ids: HashSet = HashSet::new(); while ids.len() < count { ids.insert(Uuid::new_v4()); } let mut name_ids: HashMap = HashMap::new(); for id in ids.iter() { let name = Name::english(id.to_string()); let field_id = names.add_name(name.clone()).unwrap(); name_ids.insert(name, field_id); } let mut rec = Record::new(names); for id in ids.iter() { let name = Name::english(id.to_string()); rec.insert(name, id.clone()); } for (name, id) in name_ids.iter() { let id1 = rec.get(name).unwrap(); let id2 = rec.get(id).unwrap(); assert_eq!(id1, id2, "id and name should produce the same result"); match id1 { Field::Uuid(data) => { assert_eq!( data.to_string(), name.to_string(), "for this case, name and data should match" ); assert!(ids.contains(&data), "{:?} not in {:?}", id1, ids); ids.remove(&data); } _ => unreachable!("got {:?}, should have been uuid", id1), } } assert_eq!(ids.len(), 0, "did not pull {:?}", ids); } #[test] fn does_insert_error_on_bad_field_name() { let names = Names::new(); let mut rec = Record::new(names); let name = Name::english("wrong".to_string()); match rec.insert(name.clone(), "bad") { Ok(_) => unreachable!("should return not found error"), Err(err) => match err { MTTError::NameNotFound(data) => assert_eq!(data, name), _ => unreachable!("got {:?}, should have been not found", err), }, } } #[test] fn does_get_error_on_bad_field_name() { let names = Names::new(); let mut rec = Record::new(names); let name = Name::english("missing".to_string()); match rec.get(&name) { Ok(_) => unreachable!("should return not found error"), Err(err) => match err { MTTError::NameNotFound(data) => assert_eq!(data, name), _ => unreachable!("got {:?}, should have been not found", err), }, } } #[test] fn does_get_error_on_missing_data() { let mut names = Names::new(); let name = Name::english("empty".to_string()); names.add_name(name.clone()).unwrap(); let mut rec = Record::new(names); match rec.get(&name) { Ok(_) => unreachable!("should return not found error"), Err(err) => match err { MTTError::FieldMissingData => {} _ => unreachable!("got {:?}, should have been not found", err), }, } } #[test] fn can_records_hold_multiple_entries() { let field_name = Name::english("field".to_string()); let mut names = Names::new(); names.add_name(field_name.clone()); let count: isize = 5; let mut recs = RecordIter::new(); assert_eq!(recs.len(), 0, "should contain no records"); for data in 0..count { let mut rec = Record::new(names.clone()); rec.insert(field_name.clone(), data.clone()); recs.push(rec); let holder: usize = (data + 1).try_into().unwrap(); assert_eq!(recs.len(), holder, "should increase the count"); } let mut data = 0; for rec in recs { let item = rec.get(&field_name).unwrap(); assert_eq!(item, data.into()); data += 1; } assert_eq!(data, count, "did not iterate") } #[test] fn errors_on_mismatched_names() { let count = 2; let mut recs: Vec = Vec::new(); for i in 0..count { let name = Name::english(i.to_string()); let mut names = Names::new(); names.add_name(name); recs.push(Record::new(names)); } let mut rec_iter = RecordIter::new(); rec_iter.push(recs[0].clone()).unwrap(); match rec_iter.push(recs[1].clone()) { Ok(_) => unreachable!("should have returned error"), Err(err) => match err { MTTError::RecordMismatch => {} _ => unreachable!("got {:?}, should have gotten mismatched names", err), }, } } #[test] fn errors_on_mismatched_entries() { let mut names = Names::new(); let name1 = Name::english("first".to_string()); let name2 = Name::english("second".to_string()); names.add_name(name1.clone()).unwrap(); names.add_name(name2.clone()).unwrap(); let mut rec1 = Record::new(names.clone()); rec1.insert(&name1, 1); rec1.insert(&name2, 2); let mut rec2 = Record::new(names.clone()); rec2.insert(&name1, 1); let mut rec_iter = RecordIter::new(); rec_iter.push(rec1).unwrap(); match rec_iter.push(rec2) { Ok(_) => unreachable!("should have returned error"), Err(err) => match err { MTTError::RecordMismatch => {} _ => unreachable!("got {:?}, should have gotten mismatched names", err), }, } } } #[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) -> Option where NT: Into, { match self.data.get(&name.into()) { Some(data) => Some(data.get()), None => 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())); } output } fn iter(&self) -> DocIter { DocIter::new(self) } 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()); let data = Uuid::new_v4().to_string(); add.add_field(name.clone(), data.clone()); let result = add.get_field(&name).unwrap(); match result { Field::StaticString(result) => assert_eq!(result, data), _ => unreachable!("got {:?}: should have received static string", result), } } fn can_add_uuid() { let mut add = Document::new(); let name = Name::english(Uuid::new_v4().to_string()); let data = Uuid::new_v4(); add.add_field(name.clone(), data.clone()); let result = add.get_field(&name).unwrap(); match result { Field::Uuid(result) => assert_eq!(result, data), _ => unreachable!("got {:?}: should have received uuid", result), } } } #[derive(Clone, Debug)] struct Delete { query: Query, } impl Delete { fn new() -> Self { Self { query: Query::new(), } } fn get_query(&self) -> &Query { &self.query } fn get_query_mut(&mut self) -> &mut Query { &mut self.query } } #[derive(Clone, Debug)] struct Update { query: Query, values: Document, } impl Update { fn new() -> Self { Self { query: Query::new(), values: Document::new(), } } fn get_query(&self) -> &Query { &self.query } fn get_query_mut(&mut self) -> &mut Query { &mut 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(), } } } 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 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("".to_string(), field)); } else { oids.insert(oid); } Ok(()) } 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) -> HashSet { let mut output = HashSet::new(); for (key, value) in self.data.iter() { let mut op = calc.clone(); op.add_value(key.clone()); if op.calculate() == true.into() { output = output.union(&value).cloned().collect(); } } 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("".to_string(), field.clone())), 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 pull(&self, field_id: &Uuid, calc: &Calculation) -> HashSet { self.get_index(field_id).pull(calc) } fn add_to_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.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(()) } } #[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(field_name, value) => { assert_eq!(field_name, ""); assert_eq!(value, field); } _ => 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(field_name, value) => { assert_eq!(field_name, ""); assert_eq!(value, field); } _ => unreachable!("got {:?}: should have been duplicate field", err), }, } } } struct DocumentFile { docdef: DocDef, docs: HashMap, indexes: Indexes, queue: Queue, rx: Receiver, } impl DocumentFile { fn new(queue: Queue, rx: Receiver, docdef: DocDef) -> Self { Self { docdef: docdef.clone(), docs: HashMap::new(), indexes: docdef.create_indexes(), queue: queue, 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 name = docdef.get_document_name(); let id = queue.add_sender(tx); let reg_msg = Register::new(id, RegMsg::AddDocName(name.clone())); let msg = Message::new(NameType::None, reg_msg.clone()); queue.send(msg.clone()).unwrap(); rx.recv().unwrap(); let routes = [ Path::new( Include::All, Include::Some(name.into()), Include::Some(Action::Addition), ), Path::new( Include::All, Include::Some(name.into()), Include::Some(Action::Delete), ), Path::new( Include::All, Include::Some(name.into()), Include::Some(Action::Query), ), Path::new( Include::All, Include::Some(name.into()), Include::Some(Action::Show), ), Path::new( Include::All, Include::Some(name.into()), Include::Some(Action::Update), ), ]; for route in routes.iter() { let request = reg_msg.response(RegMsg::AddRoute(route.clone())); let add_route = msg.response(request); queue.send(add_route).unwrap(); rx.recv().unwrap(); } let mut doc = DocumentFile::new(queue.clone(), rx, docdef); spawn(move || { doc.listen(); }); let reply = msg.response(Reply::new()); queue.send(reply).unwrap(); } fn listen(&mut self) { loop { let msg = self.rx.recv().unwrap(); let result = match msg.get_action() { MsgAction::Addition(data) => self.add_document(data), MsgAction::Delete(delete) => self.delete(delete), MsgAction::Query(query) => self.query(query), MsgAction::Update(update) => self.update(update), _ => Reply::new().into(), }; self.queue.send(msg.response(result)).unwrap(); } } fn get_docdef(&self) -> &DocDef { &self.docdef } /* fn get_documents<'a>(&self) -> impl Iterator { self.docs.iter() } */ fn validate(&self, field_name: NT, value: Option) -> Result<(Uuid, Field), MTTError> 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((field_id, output)) } fn add_field_to_error(key: String, err: MTTError) -> MTTError { match err { MTTError::DocumentFieldMissing(_) => MTTError::DocumentFieldMissing(key), MTTError::FieldDuplicate(_, field) => MTTError::FieldDuplicate(key, field.clone()), _ => 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) } 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, addition: &Addition) -> MsgAction { let mut holder = InternalRecord::new(); let doc = addition.get_document(); for (field, value) in doc.iter() { match self.validate(field, Some(value)) { Ok((id, data)) => { holder.insert(id, data); } Err(err) => return MsgAction::Error(err), } } let requested: HashSet = holder.keys().cloned().collect(); let all_fields = self.docdef.field_ids(); for field in all_fields.difference(&requested).cloned() { match self.validate(field, None) { Ok((id, data)) => { holder.insert(id, data); } Err(err) => return MsgAction::Error(err), } } let mut records = RecordIter::with_names(self.docdef.get_field_names().clone()); if !holder.is_empty() { let mut oid = Oid::new(); while self.docs.contains_key(&oid) { oid = Oid::new(); } self.docs.insert(oid, holder.clone()); records.insert(holder); } records.into() } fn delete(&mut self, delete: &Delete) -> MsgAction { let mut reply = Reply::new(); /* let oids = self.run_query(delete.get_query()).unwrap(); for oid in oids.iter() { reply.add(self.docs.get(oid).unwrap().clone()); self.docs.remove(oid); } */ reply.into() } fn run_query(&self, query: &Query) -> Result, MTTError> { 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 = self.indexes.pull(field_id, calculation); oids = oids.intersection(&holder).cloned().collect(); } for (field_id, calculation) in unindexed.iter() { for oid in oids.clone().iter() { let doc = self.docs.get(oid).unwrap(); let mut calc = calculation.clone(); calc.add_value(doc.get(field_id).unwrap().clone()); if calc.calculate() == false.into() { oids.remove(oid); } } } Ok(oids) /* let query_ids = query.field_ids(); let doc_ids = self.docdef.field_ids(); let index_ids = self.indexes.index_ids(); if !doc_ids.is_superset(&query_ids) { let missed = query_ids.difference(&doc_ids).last().unwrap(); return Err(MTTError::DocumentFieldNotFound(missed.to_string())); } let used_indexed = index_ids .intersection(&query_ids) .cloned() .collect::>(); let used_unindexed = query_ids .difference(&index_ids) .cloned() .collect::>(); let mut oids = HashSet::new(); if used_indexed.is_empty() { 'docs: for (oid, doc) in self.docs.iter() { for query_id in query_ids.iter() { let doc_data = doc.get_field(query_id).unwrap(); let mut operation = query.get(query_id).unwrap(); match operation.add_value(doc_data.clone()) { Ok(_) => {} Err(err) => match err { MTTError::DocumentFieldWrongDataType(got, expected) => { return Err(MTTError::DocumentFieldWrongDataType(expected, got)) } _ => return Err(err), }, } if operation.calculate() == false.into() { continue 'docs; } } oids.insert(oid.clone()); } } else { let mut first_time = true; for field_id in used_indexed.iter() { let op = query.get(field_id).unwrap(); let holder = self.indexes.pull(field_id, &op); if first_time { oids = holder; } else { oids = oids.intersection(&holder).cloned().collect(); } first_time = false; } for field_id in used_unindexed.iter() { let mut holder: HashSet = HashSet::new(); for oid in oids.iter() { let doc = self.docs.get(oid).unwrap(); let mut op = query.get(field_id).unwrap().clone(); op.add_value(doc.get_field(field_id).unwrap()); if op.calculate() == true.into() { holder.insert(oid.clone()); } } oids = oids.intersection(&holder).cloned().collect(); } } Ok(oids) */ } fn query(&self, query: &Query) -> MsgAction { let mut records = RecordIter::with_names(self.docdef.get_field_names().clone()); 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.into(), }; 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 = self.indexes.pull(field_id, calculation); oids = oids.intersection(&holder).cloned().collect(); } for (field_id, calculation) in unindexed.iter() { for oid in oids.clone().iter() { let doc = self.docs.get(oid).unwrap(); let mut calc = calculation.clone(); calc.add_value(doc.get(field_id).unwrap().clone()); if calc.calculate() == false.into() { oids.remove(oid); } } } for oid in oids.iter() { records.insert(self.docs.get(oid).unwrap().clone()); } records.into() } fn update(&mut self, update: &Update) -> MsgAction { /* let oids = match self.run_query(update.get_query()) { Ok(result) => result, Err(err) => return err.into(), }; let mut holder: HashMap = HashMap::new(); for oid in oids.iter() { let doc = self.docs.get(oid).unwrap(); let old_new = [doc.clone(), doc.clone()]; holder.insert(oid.clone(), old_new); } let mut index_holder = self.docdef.create_indexes(); for (oid, docs) in holder.iter_mut() { let mut updated = Document::new(); for (key, value) in update.get_values().iter() { match self.validate(&key, Some(value.clone())) { Ok(field) => { let id = self.docdef.get_field_id(&key).unwrap(); match index_holder.validate(&id, &field) { Ok(_) => { index_holder.add_to_index(&id, field.clone(), oid.clone()); docs[1].add_field(key.clone(), field.clone()); } Err(err) => return Self::add_field_to_error(key.to_string(), err).into(), } } Err(err) => return err.into(), } } } let mut reply = Reply::new(); for (oid, docs) in holder.iter() { self.docs.insert(oid.clone(), docs[1].clone()); reply.add(docs[1].clone()); for (key, value) in docs[0].iter() { self.remove_from_index(&key, &value, oid); self.add_to_index(&key, docs[1].get_field(&key).unwrap().clone(), oid.clone()); } } reply.into() */ Reply::new().into() } } #[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, routes: Vec, sender_id: Uuid, rx: Receiver, } impl TestDocument { fn new(field_types: Vec) -> Self { let doc_name = Name::english(Uuid::new_v4().to_string()); 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)), 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, routes: standard_paths(), 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_routes_mut(&mut self) -> &mut Vec { &mut self.routes } fn get_queue(&mut self) -> Queue { self.queue.clone() } fn get_receiver(&self) -> &Receiver { &self.rx } 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_name().clone(), action); self.queue.send(msg) } fn start(&mut self) { let msg = Message::new(self.docdef.get_document_name().clone(), self.docdef.clone()); DocumentFile::start(self.queue.clone(), msg); for route in self.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)), item.clone()); count += 1; } self.send(add).unwrap(); self.rx.recv().unwrap(); // eat addition response. } } 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, routes: standard_paths(), sender_id: id, rx: rx, } } } #[test] fn does_not_respond_to_create() { let name = Name::english("quiet".to_string()); let docdef = DocDef::new(name.clone()); let mut test_doc: TestDocument = docdef.into(); let alt = Name::english("alternate".to_string()); test_doc.start(); 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".to_string()); let docdef = DocDef::new(name.clone()); let mut test_doc: TestDocument = docdef.into(); test_doc.start(); let queue = test_doc.get_queue(); let msg_actions = [ MsgAction::Addition(Addition::new()), MsgAction::Delete(Delete::new()), MsgAction::Query(Query::new()), MsgAction::Show, MsgAction::Update(Update::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".to_string()); let alt = Name::english("alternate".to_string()); let docdef = DocDef::new(name.clone()); let mut test_doc: TestDocument = docdef.into(); test_doc.start(); let queue = test_doc.get_queue(); let reg_msg = Register::new(test_doc.get_sender_id(), RegMsg::AddDocName(alt.clone())); 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::Delete(Delete::new()), MsgAction::Query(Query::new()), MsgAction::Show, MsgAction::Update(Update::new()), ]; for msg_action in msg_actions.iter() { let msg = Message::new(alt.clone(), msg_action.clone()); queue.send(msg).unwrap(); match test_doc.get_receiver().recv_timeout(TIMEOUT) { Ok(msg) => unreachable!("for {:?} should not receive: {:?}", msg_action, msg), Err(err) => match err { RecvTimeoutError::Timeout => {} _ => unreachable!("should have timed out"), }, } } } #[test] fn can_document_be_added() { let doc_name = Name::english("document".to_string()); let mut docdef = DocDef::new(doc_name.clone()); let name = Name::english("field".to_string()); let data = Uuid::new_v4(); docdef.add_field(name.clone(), FieldType::Uuid); let mut test_doc: TestDocument = docdef.clone().into(); test_doc.start(); 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.clone() { 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".to_string()); let mut docdef = DocDef::new(doc_name.clone()); let name = Name::english("count".to_string()); docdef.add_field(name.clone(), FieldType::Integer); let mut test_doc: TestDocument = docdef.clone().into(); test_doc.start(); 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.clone() { 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(); let queue = test_doc.get_queue(); let name = Name::english("bad".to_string()); let mut addition = Addition::new(); addition.add_field(name.clone(), "doesn't matter"); queue.send(Message::new(test_doc.get_docdef().get_document_name(), 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(); let queue = test_doc.get_queue(); let mut addition = Addition::new(); addition.add_field(Name::english("field0".to_string()), "string"); queue.send(Message::new(test_doc.get_docdef().get_document_name(), 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(); let queue = test_doc.get_queue(); let mut addition = Addition::new(); addition.add_field(Name::english("field0".to_string()), 1); queue.send(Message::new(test_doc.get_docdef().get_document_name(), 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(); 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()); let mut query = Query::new(); query.add(Name::english("field0".to_string()), calc); queue.send(Message::new(test_doc.get_docdef().get_document_name(), 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.clone() { assert_eq!(doc.get(&Name::english("field0".to_string())).unwrap(), expected.into()); } } _ => unreachable!("got {:?}: should have been a reply", action), } } /* #[test] fn gets_all_documents_in_query() { let mut doc = TestDocument::new([FieldType::Integer].to_vec()); doc.start(); let values = [ [1.into()].to_vec(), [2.into()].to_vec(), [1.into()].to_vec(), [3.into()].to_vec(), [1.into()].to_vec(), [4.into()].to_vec(), [1.into()].to_vec(), [5.into()].to_vec(), ]; for value in values.iter() { doc.populate(value.clone()); } let mut calc = Calculation::new(Operand::Equal); calc.add_value(1); let mut query = Query::new(); query.add("field0".to_string(), calc); doc.send(query).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); let input: Field = 1.into(); match action { MsgAction::Reply(data) => { assert_eq!(data.len(), 4, "should return 4 entries"); for doc in data.iter() { assert_eq!(doc.get_field("field0").unwrap(), input); } } _ => 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(); 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"); query.add("field0".to_string(), calc); let mut calc = Calculation::new(Operand::Equal); calc.add_value("b"); query.add("field1".to_string(), calc); doc.send(query).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Reply(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_field("field0").unwrap(), afield); assert_eq!(doc.get_field("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("field0".to_string(), IndexType::Index); docdef.add_index("field1".to_string(), IndexType::Index); doc.start(); 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"); query.add("field0".to_string(), calc); let mut calc = Calculation::new(Operand::Equal); calc.add_value("b"); query.add("field1".to_string(), calc); doc.send(query).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Reply(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_field("field0").unwrap(), afield); assert_eq!(doc.get_field("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("field0".to_string(), IndexType::Index); doc.start(); 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"); query.add("field0".to_string(), calc); let mut calc = Calculation::new(Operand::Equal); calc.add_value("b"); query.add("field1".to_string(), calc); doc.send(query).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Reply(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_field("field0").unwrap(), afield); assert_eq!(doc.get_field("field1").unwrap(), bfield); } } _ => unreachable!("got {:?}: should have been a reply", action), } } #[test] fn errors_on_bad_field_name() { let (docdef, doc_name) = create_docdef(Vec::new()); let field_name = "wrong"; let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); let mut query = Query::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value("something"); query.add(field_name.to_string(), 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::DocumentFieldNotFound(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(); 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("field0".to_string(), 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::DocumentFieldWrongDataType(expected, got) => { assert_eq!(expected, &FieldType::Uuid); assert_eq!(got, &FieldType::StaticString); } _ => unreachable!("got {:?}: should been field not found", data), }, _ => unreachable!("got {:?}: should have been a error", action), } } #[test] fn can_use_default_values() { let (mut docdef, doc_name) = create_docdef([FieldType::StaticString].to_vec()); let mut calc = Calculation::new(Operand::Assign); calc.add_value(FieldType::StaticString); docdef.set_default("field0", calc); let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); 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::Reply(docs) => { assert_eq!(docs.len(), 1); for doc in docs.iter() { let expected: Field = "".into(); assert_eq!(doc.get_field("field0").unwrap(), expected); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } } #[test] fn can_a_default_value_be_set() { let (mut docdef, doc_name) = create_docdef([FieldType::Uuid].to_vec()); let input = Uuid::nil(); let mut calc = Calculation::new(Operand::Assign); calc.add_value(input.clone()); docdef.set_default("field0", calc); let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); 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::Reply(docs) => { assert_eq!(docs.len(), 1); let expected: Field = input.into(); for doc in docs.iter() { assert_eq!(doc.get_field("field0").unwrap(), expected); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } } #[test] fn can_default_values_be_overridden() { let (mut docdef, doc_name) = create_docdef([FieldType::Uuid].to_vec()); let mut calc = Calculation::new(Operand::Assign); calc.add_value(FieldType::Uuid); docdef.set_default("field0", calc); let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); let mut new_doc = Addition::new(); new_doc.add_field("field0".to_string(), 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::Reply(docs) => { assert_eq!(docs.len(), 1); let expected: Field = Uuid::nil().into(); for doc in docs.iter() { assert_eq!(doc.get_field("field0").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(); for id in ids.iter() { doc.populate([id.clone().into()].to_vec()); } let mut update = Update::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value(id); update.get_query_mut().add("field0".to_string(), calc); update .get_values_mut() .add_field("field0".to_string(), Uuid::nil()); doc.send(update).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Reply(docs) => assert_eq!(docs.len(), 0), _ => unreachable!("got {:?}: should have gotten a reply", action), } } #[test] fn only_responses_to_its_update_request() { let mut doc = TestDocument::new([FieldType::Integer].to_vec()); doc.start(); let alt_doc_name = "alternate"; let (tx, _) = channel(); let mut queue = doc.get_queue(); queue .register(tx, alt_doc_name.to_string(), Vec::new()) .unwrap(); let update = Update::new(); let msg = Message::new(alt_doc_name, update); queue.send(msg).unwrap(); match 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 changes_information_requested() { let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); doc.start(); let id = Uuid::new_v4(); let old = "old"; let new = "new"; doc.populate([id.into(), old.into()].to_vec()); let mut update = Update::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value(id.clone()); update .get_query_mut() .add("field0".to_string(), calc.clone()); update.get_values_mut().add_field("field1".to_string(), new); doc.send(update).unwrap(); let mut results: HashMap = HashMap::new(); results.insert( "update".to_string(), doc.get_receiver().recv_timeout(TIMEOUT).unwrap(), ); let mut query = Query::new(); query.add("field0".to_string(), calc.clone()); doc.send(query).unwrap(); results.insert( "query".to_string(), doc.get_receiver().recv_timeout(TIMEOUT).unwrap(), ); let expected_id: Field = id.into(); let output: Field = new.into(); for (key, result) in results.iter() { let action = result.get_action(); match action { MsgAction::Reply(docs) => { assert_eq!(docs.len(), 1, "{}", key); for doc in docs.iter() { assert_eq!(doc.get_field("field0").unwrap(), expected_id, "{}", key); assert_eq!(doc.get_field("field1").unwrap(), output, "{}", key); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } } } #[test] fn changes_only_the_queried() { let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); doc.start(); let mut ids: HashSet = HashSet::new(); while ids.len() < 2 { ids.insert(Uuid::new_v4()); } let expected = ids.iter().last().unwrap(); let old = "old"; let new = "new"; let mut values: Vec> = Vec::new(); for id in ids.iter() { let mut holder: Vec = Vec::new(); holder.push(id.clone().into()); holder.push(old.into()); values.push(holder); } for value in values { doc.populate(value); } let mut update = Update::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value(expected.clone()); update.get_query_mut().add("field0".to_string(), calc); update.get_values_mut().add_field("field1".to_string(), new); doc.send(update).unwrap(); doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let mut calc = Calculation::new(Operand::Equal); calc.add_value(new); let mut query = Query::new(); query.add("field1".to_string(), calc); doc.send(query).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Reply(docs) => { assert_eq!(docs.len(), 1); let expected_id: Field = expected.clone().into(); let output: Field = new.into(); for doc in docs.iter() { assert_eq!(doc.get_field("field0").unwrap(), expected_id); assert_eq!(doc.get_field("field1").unwrap(), output); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } } #[test] fn can_handle_multiple_updates() { let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); doc.start(); let count = 3; let id = Uuid::new_v4(); let old = "old"; let new = "new"; for _ in 0..count { doc.populate([id.into(), old.into()].to_vec()); } let mut calc = Calculation::new(Operand::Equal); calc.add_value(id.clone()); let mut update = Update::new(); update .get_query_mut() .add("field0".to_string(), calc.clone()); update.get_values_mut().add_field("field1".to_string(), new); doc.send(update).unwrap(); let mut results: HashMap = HashMap::new(); results.insert( "update".to_string(), doc.get_receiver().recv_timeout(TIMEOUT).unwrap(), ); let mut query = Query::new(); query.add("field0".to_string(), calc.clone()); doc.send(query).unwrap(); results.insert( "query".to_string(), doc.get_receiver().recv_timeout(TIMEOUT).unwrap(), ); let expected_id: Field = id.into(); let output: Field = new.into(); for (key, result) in results.iter() { let action = result.get_action(); match action { MsgAction::Reply(docs) => { assert_eq!(docs.len(), count, "{}", key); for doc in docs.iter() { assert_eq!(doc.get_field("field0").unwrap(), expected_id, "{}", key); assert_eq!(doc.get_field("field1").unwrap(), output, "{}", key); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } } } #[test] fn update_errors_on_bad_field_name() { let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); doc.start(); let id = Uuid::new_v4(); let old = "old"; let new = "new"; doc.populate([id.into(), old.into()].to_vec()); let mut update = Update::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value(id.clone()); update.get_query_mut().add("field0".to_string(), calc); update.get_values_mut().add_field("wrong".to_string(), 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::DocumentFieldNotFound(data) => assert_eq!(data, "wrong"), _ => 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(); let id = Uuid::new_v4(); let old = "old"; let new = Uuid::nil(); doc.populate([id.into(), old.into()].to_vec()); let mut update = Update::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value(id.clone()); update.get_query_mut().add("field0".to_string(), calc); update.get_values_mut().add_field("field1".to_string(), 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 docdef, doc_name) = create_docdef([FieldType::Uuid].to_vec()); docdef.add_index("field0".to_string(), IndexType::Unique); let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); let field0 = Uuid::new_v4(); let mut addition = Addition::new(); addition.add_field("field0".to_string(), field0.clone()); let msg = Message::new(doc_name.clone(), addition.clone()); queue.send(msg).unwrap(); rx.recv_timeout(TIMEOUT).unwrap(); let msg2 = Message::new(doc_name.clone(), addition); queue.send(msg2).unwrap(); let result = rx.recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Error(err) => match err { MTTError::FieldDuplicate(key, field) => { let expected: Field = field0.into(); assert_eq!(key, "field0"); assert_eq!(field, &expected); } _ => unreachable!("got {:?}: should have gotten an missing field", err), }, _ => unreachable!("got {:?}: should have gotten an error", action), } } #[test] fn unique_value_remains_available_if_failure_occurs() { let (mut docdef, doc_name) = create_docdef([FieldType::Uuid, FieldType::Uuid].to_vec()); docdef.add_index("field0".to_string(), IndexType::Unique); let (queue, rx) = test_doc(doc_name.as_str(), docdef, standard_routes()); let field0 = Uuid::new_v4(); let mut bad_addition = Addition::new(); bad_addition.add_field("field0".to_string(), field0.clone()); bad_addition.add_field("field1".to_string(), ""); let msg = Message::new(doc_name.clone(), bad_addition.clone()); queue.send(msg).unwrap(); rx.recv_timeout(TIMEOUT).unwrap(); let mut good_addition = Addition::new(); good_addition.add_field("field0".to_string(), field0.clone()); good_addition.add_field("field1".to_string(), field0.clone()); let msg = Message::new(doc_name.clone(), good_addition.clone()); queue.send(msg).unwrap(); let result = rx.recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Reply(_) => {} _ => unreachable!("got {:?}: should have gotten a reply", action), } } #[test] fn updating_unique_updates_index_entries() { let mut doc = TestDocument::new([FieldType::StaticString].to_vec()); doc.get_docdef_mut() .add_index("field0".to_string(), IndexType::Unique); doc.start(); let old = "old"; let new = "new"; let fold: Field = old.into(); let fnew: Field = new.into(); doc.populate([old.into()].to_vec()); let mut update = Update::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value(old); update.get_query_mut().add("field0".to_string(), calc); update.get_values_mut().add_field("field0".to_string(), new); doc.send(update).unwrap(); doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let mut old_addition = Addition::new(); old_addition.add_field("field0".to_string(), old); doc.send(old_addition).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Reply(data) => { assert_eq!(data.len(), 1); for doc in data.iter() { assert_eq!(doc.get_field("field0").unwrap(), fold); } } _ => unreachable!("got {:?}: should have gotten a reply", action), } let mut new_addition = Addition::new(); new_addition.add_field("field0".to_string(), 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(key, field) => { let expected: Field = new.into(); assert_eq!(key, "field0"); assert_eq!(field, &expected); } _ => unreachable!("got {:?}: should have gotten an missing field", err), }, _ => unreachable!("got {:?}: should have gotten an error", action), } } #[test] fn unique_available_after_bad_change() { let mut doc = TestDocument::new([FieldType::Uuid, FieldType::StaticString].to_vec()); doc.get_docdef_mut() .add_index("field0".to_string(), IndexType::Unique); doc.start(); 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 update = Update::new(); let mut calc = Calculation::new(Operand::Equal); calc.add_value(data); update.get_query_mut().add("field1".to_string(), calc); update .get_values_mut() .add_field("field0".to_string(), 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(key, field) => { assert_eq!(key, "field0"); assert_eq!(field, &fholder); } _ => unreachable!("got {:?}: should have gotten an missing field", 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::Reply(data) => { assert_eq!(data.len(), ids.len()); for doc in data.iter() { match doc.get_field("field0").unwrap() { Field::Uuid(id) => assert!(ids.contains(&id)), _ => unreachable!("did not get uuid"), } } } _ => unreachable!("got {:?}: should have gotten reply", action), } } #[test] fn can_calculate_field_values() { let mut doc = TestDocument::new([FieldType::DateTime].to_vec()); doc.start(); 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("field0".to_string(), 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::Reply(data) => { assert_eq!(data.len(), 1); for doc in data.iter() { match doc.get_field("field0").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 mut doc = TestDocument::new([FieldType::Integer].to_vec()); doc.start(); doc.populate([1.into()].to_vec()); let mut calc = Calculation::new(Operand::Equal); calc.add_value(1); let mut query = Query::new(); query.add("field0".to_string(), calc); let mut delete = Delete::new(); *delete.get_query_mut() = query.clone(); doc.send(delete).unwrap(); let result = doc.get_receiver().recv_timeout(TIMEOUT).unwrap(); let action = result.get_action(); match action { MsgAction::Reply(data) => { assert_eq!(data.len(), 1); for doc in data.iter() { match doc.get_field("field0").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::Reply(data) => assert_eq!(data.len(), 0), _ => unreachable!("did not get uuid"), } } #[test] fn delete_should_only_respond_to_its_own() { let mut doc = TestDocument::new([FieldType::Integer].to_vec()); doc.start(); doc.populate([1.into()].to_vec()); let mut calc = Calculation::new(Operand::Equal); calc.add_value(1); let mut query = Query::new(); query.add("field0".to_string(), calc); let mut delete = Delete::new(); *delete.get_query_mut() = query.clone(); doc.send(delete).unwrap(); let name = "other"; let msg = Message::new(name.to_string(), MsgAction::Show); let (tx, _) = channel(); let mut queue = doc.get_queue(); queue.register(tx, name.to_string(), Vec::new()).unwrap(); queue.send(msg).unwrap(); match doc.get_receiver().recv_timeout(TIMEOUT) { Ok(msg) => unreachable!("should not receive: {:?}", msg), Err(err) => match err { RecvTimeoutError::Timeout => {} _ => unreachable!("should have timed out"), }, } } */ } #[cfg(test)] mod createdocs { use super::support_test::TIMEOUT; use super::*; fn setup_create_doc(routes: Vec) -> (Queue, Receiver) { let mut queue = Queue::new(); let (tx, rx) = channel(); /* queue .register(tx, Uuid::new_v4().to_string(), routes) .unwrap(); */ CreateDoc::start(queue.clone()); (queue, rx) } #[test] #[ignore] fn create_document_creation() { let name = Name::english("project".to_string()); let routes = [RouteRequest::new( Include::All, Include::All, Include::Some(Action::Reply), )] .to_vec(); let (queue, rx) = setup_create_doc(routes); let msg1 = Message::new(name.clone(), 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()); assert_eq!(result1.get_document_id(), msg1.get_document_id()); match result1.get_action() { MsgAction::Reply(_) => {} _ => unreachable!("got {:?}: should have been a reply.", result1.get_action()), } let msg2 = Message::new(name, MsgAction::Query(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(), msg2.get_document_id()); match result2.get_action() { MsgAction::Reply(_) => {} _ => unreachable!("got {:?}: should have been a reply.", result1.get_action()), } } #[test] #[ignore] fn does_duplicates_generate_error() { let name = Name::english("duplicate".to_string()); let routes = [ RouteRequest::new(Include::All, Include::All, Include::Some(Action::Reply)), RouteRequest::new(Include::All, Include::All, Include::Some(Action::Error)), ] .to_vec(); let (queue, rx) = setup_create_doc(routes); let msg = Message::new(name.clone(), MsgAction::Create(DocDef::new(name.clone()))); queue.send(msg.clone()).unwrap(); rx.recv_timeout(TIMEOUT).unwrap(); queue.send(msg.clone()).unwrap(); let result = rx.recv_timeout(TIMEOUT).unwrap(); assert_eq!(result.get_message_id(), msg.get_message_id()); assert_eq!(result.get_document_id(), msg.get_document_id()); match result.get_action() { MsgAction::Error(err) => match err { MTTError::DocumentAlreadyExists(data) => assert_eq!(data, &name.to_string()), _ => unreachable!("got {:?}: should have been a reply.", err), }, _ => unreachable!("got {:?}: should have been a reply.", result.get_action()), } } }