use crate::{ action::{CalcValue, MsgAction, Operand, Query}, document::field::{Field, FieldType}, mtterror::MTTError, name::{NameType, Names}, queue::data_director::{Include, Path, Route}, }; use chrono::prelude::*; use std::{ collections::{HashMap, HashSet}, time::Duration, }; use uuid::Uuid; #[derive(Clone, Debug)] pub struct Message { msg_id: Uuid, document_id: NameType, action: MsgAction, route: Route, // session: Option } impl Message { pub 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(), route: Route::default(), } } pub fn get_message_id(&self) -> &Uuid { &self.msg_id } pub fn get_action(&self) -> &MsgAction { &self.action } pub fn get_path(&self) -> Path { Path::new( Include::Just(self.msg_id.clone()), Include::Just(self.document_id.clone()), Include::Just(self.action.clone().into()), ) } pub fn get_route(&self) -> Route { self.route.clone() } pub fn set_route(&mut self, route: Route) { self.route = route; } pub 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(), route: Route::default(), } } pub fn forward(&self, doc_id: D, action: A) -> Self where D: Into, A: Into, { Self { msg_id: self.msg_id.clone(), document_id: doc_id.into(), action: action.into(), route: Route::default(), } } } #[cfg(test)] mod messages { use super::*; use crate::{document::definition::DocDef, name::Name}; #[test] fn can_the_document_be_a_named_reference() { let dts = [Name::english("one"), Name::english("two")]; for document in dts.into_iter() { let msg = Message::new( document.clone(), MsgAction::Create(DocDef::new(document.clone())), ); match &msg.document_id { NameType::Name(data) => assert_eq!(data, &document), _ => unreachable!("should have been a string id"), } match msg.get_action() { MsgAction::Create(_) => {} _ => unreachable!("should have been a create document"), } } } #[test] fn can_the_document_be_an_id() { let document = Uuid::new_v4(); let msg = Message::new(document.clone(), Query::new()); match msg.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 do_messages_contain_routes() { let mut msg = Message::new(Name::english("whatever"), Query::new()); let default_route = msg.get_route(); match default_route.msg_id { Include::Just(_) => unreachable!("should defalt to all"), Include::All => {} } match default_route.doc_id { Include::Just(_) => unreachable!("should defalt to all"), Include::All => {} } match default_route.action { Include::Just(_) => unreachable!("should defalt to all"), Include::All => {} } let doc_id = Uuid::new_v4(); let route = Route::new( Include::Just(msg.get_message_id().clone()), Include::Just(doc_id.clone()), Include::Just(msg.get_action().into()), ); msg.set_route(route); let result = msg.get_route(); match result.msg_id { Include::Just(data) => assert_eq!(&data, msg.get_message_id()), Include::All => unreachable!("should have message id"), } match result.doc_id { Include::Just(data) => assert_eq!(data, doc_id), Include::All => unreachable!("should have document id"), } match result.action { Include::Just(data) => assert_eq!(data, msg.get_action().into()), Include::All => unreachable!("should have action"), } } #[test] fn is_the_message_id_random() { let mut ids: Vec = Vec::new(); for _ in 0..5 { let msg = Message::new(Name::english("tester"), Query::new()); let id = msg.get_message_id().clone(); assert!(!ids.contains(&id), "{:?} containts {}", ids, id); ids.push(id); } } #[test] fn can_make_reply_message() { let name = Name::english("testing"); let msg = Message::new(name.clone(), Query::new()); let responce = Reply::new(); let reply = msg.response(responce); assert_eq!(reply.get_message_id(), msg.get_message_id()); match &reply.document_id { NameType::Name(data) => assert_eq!(data, &name), _ => unreachable!("should have been a name"), } match reply.get_action() { MsgAction::Reply(_) => {} _ => unreachable!("should have been a reply"), } } #[test] fn can_make_error_message() { let name = Name::english("testing"); let msg = Message::new(name.clone(), Query::new()); let err_msg = Uuid::new_v4().to_string(); let result = msg.response(MTTError::DocumentNotFound(err_msg.clone())); assert_eq!(result.get_message_id(), msg.get_message_id()); match &result.document_id { NameType::Name(data) => assert_eq!(data, &name), _ => unreachable!("should have been a name"), } match result.get_action() { MsgAction::Error(data) => match data { MTTError::DocumentNotFound(txt) => assert_eq!(txt, &err_msg), _ => unreachable!("got {:?}, should have received not found", data), }, _ => unreachable!("should have been a reply"), } } #[test] fn can_make_a_response_message() { let doc_id = Uuid::new_v4(); let msg = Message::new(doc_id.clone(), Query::new()); let data = Uuid::new_v4().to_string(); let result1 = msg.response(MTTError::DocumentNotFound(data.clone())); let result2 = msg.response(Reply::new()); assert_eq!(result1.get_message_id(), msg.get_message_id()); assert_eq!(result2.get_message_id(), msg.get_message_id()); assert_eq!(result1.document_id, msg.document_id); assert_eq!(result2.document_id, msg.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)] pub struct Addition { data: Document, } impl Addition { pub fn new() -> Self { Self { data: Document::new(), } } #[allow(dead_code)] pub fn add_field(&mut self, name: NT, field: CV) where CV: Into, NT: Into, { self.data.add_field(name, field); } #[allow(dead_code)] fn get_field(&self, name: NT) -> &CalcValue where NT: Into, { self.data.get_field(name) } #[allow(dead_code)] fn get_document(&self) -> Document { self.data.clone() } pub fn iter(&self) -> impl Iterator { self.data.iter() } } #[cfg(test)] mod additions { use super::*; use crate::name::Name; #[test] fn can_add_static_string() { let mut add = Addition::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4().to_string(); add.add_field(name.clone(), data.clone()); let result = add.get_field(&name); match result { CalcValue::Value(result) => match result { Field::StaticString(output) => assert_eq!(output, &data), _ => unreachable!("got {:?}, should have been a string", result), }, _ => unreachable!("got {:?}: should have received value", result), } } #[test] fn can_add_uuid() { let mut add = Addition::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4(); add.add_field(name.clone(), data.clone()); let output = add.get_field(&name); match output { CalcValue::Value(result) => match result { Field::Uuid(result) => assert_eq!(result, &data), _ => unreachable!("got {:?}: should have received uuid", result), }, _ => unreachable!("got {:?}: should have received value", output), } } #[test] fn can_get_document() { let mut add = Addition::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4(); add.add_field(name.clone(), data.clone()); let doc = add.get_document(); let output = doc.get_field(&name); match output { CalcValue::Value(holder) => match holder { Field::Uuid(result) => assert_eq!(result, &data), _ => unreachable!("should have received uuid"), }, _ => unreachable!("got {:?}: should have received value", output), } } } #[allow(dead_code)] #[derive(Clone, Debug)] pub struct Operation { field_name: String, operation: Operand, value: Field, } #[allow(dead_code)] impl Operation { pub 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() } pub fn validate(&self, field: &Field) -> bool { self.operation.validate(field, &self.value) } } #[allow(dead_code)] #[derive(Clone, Debug)] enum QueryType { Query(Query), Oids(HashSet), } impl From for QueryType { fn from(value: Query) -> Self { QueryType::Query(value) } } impl From> for QueryType { fn from(value: HashSet) -> Self { QueryType::Oids(value) } } #[allow(dead_code)] #[derive(Clone, Debug)] pub struct Reply { data: Vec, } #[allow(dead_code)] impl Reply { pub fn new() -> Self { Self { data: Vec::new() } } fn add(&mut self, doc: Document) { self.data.push(doc); } pub fn len(&self) -> usize { self.data.len() } fn iter(&self) -> impl Iterator { self.data.iter() } } #[cfg(test)] mod replies { use super::*; use crate::name::Name; #[test] fn is_new_empty() { let reply = Reply::new(); assert_eq!(reply.len(), 0, "should have no records"); } #[test] fn can_add_documents() { let mut reply = Reply::new(); let doc = Document::new(); reply.add(doc.clone()); assert_eq!(reply.len(), 1); reply.add(doc.clone()); assert_eq!(reply.len(), 2); } #[test] fn can_retrieve_documents() { let fieldname = Name::english("field"); let mut doc1 = Document::new(); doc1.add_field(fieldname.clone(), "one"); let mut doc2 = Document::new(); doc2.add_field(fieldname.clone(), "two"); let mut reply = Reply::new(); reply.add(doc1); reply.add(doc2); let mut reply_iter = reply.iter(); let result1 = reply_iter.next().unwrap(); match result1.get_field(&fieldname) { CalcValue::Value(data) => match data { Field::StaticString(output) => assert_eq!(output, "one"), _ => unreachable!("got {:?}: should have been static string", result1), }, _ => unreachable!("got {:?}, should have been value", result1), } let result2 = reply_iter.next().unwrap(); match result2.get_field(&fieldname) { CalcValue::Value(data) => match data { Field::StaticString(output) => assert_eq!(output, "two"), _ => unreachable!("got {:?}: should have been static string", result2), }, _ => unreachable!("got {:?}, should have been value", result2), } match reply_iter.next() { None => {} Some(_) => unreachable!("should be out of data"), } } } #[derive(Clone, Debug)] pub struct InternalRecord { data: HashMap, } impl InternalRecord { pub fn new() -> Self { Self { data: HashMap::new(), } } pub fn insert(&mut self, id: Uuid, data: F) -> Field where F: Into, { match self.data.insert(id, data.into()) { Some(data) => data.clone(), None => Field::None, } } pub fn get(&self, id: &Uuid) -> Option<&Field> { self.data.get(id) } pub fn is_empty(&self) -> bool { self.data.is_empty() } } #[derive(Clone, Debug)] pub struct InternalRecords { data: HashMap, } impl InternalRecords { pub fn new() -> Self { Self { data: HashMap::new(), } } pub fn insert(&mut self, oid: Oid, record: InternalRecord) -> Option { self.data.insert(oid, record) } pub fn get(&self, oid: &Oid) -> Option<&InternalRecord> { self.data.get(oid) } pub fn remove(&mut self, oid: &Oid) -> Option { self.data.remove(oid) } pub fn iter(&self) -> impl Iterator { self.data.iter() } pub fn keys(&self) -> impl Iterator { self.data.keys() } fn values(&self) -> impl Iterator { self.data.values() } pub fn contains_key(&self, oid: &Oid) -> bool { self.data.contains_key(oid) } fn len(&self) -> usize { self.data.len() } } #[derive(Clone, Debug)] pub struct Record { names: Names, data: InternalRecord, } impl Record { fn with_data(names: Names, rec: InternalRecord) -> Self { Self { names: names, data: rec, } } pub 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)] pub struct Records { names: Names, data: InternalRecords, } impl Records { pub fn new(names: Names) -> Self { Self { names: names, data: InternalRecords::new(), } } pub fn with_data(names: Names, records: InternalRecords) -> Self { Self { names: names, data: records, } } pub fn insert(&mut self, oid: Oid, record: InternalRecord) -> Option { self.data.insert(oid, record) } pub fn len(&self) -> usize { self.data.len() } pub fn iter(&self) -> impl Iterator { RecordIter::new(self) } pub fn get_internal_records(&self) -> &InternalRecords { &self.data } } struct RecordIter { names: Names, recs: Vec, } impl RecordIter { fn new(records: &Records) -> Self { Self { names: records.names.clone(), recs: records.data.values().cloned().collect(), } } } impl Iterator for RecordIter { type Item = Record; fn next(&mut self) -> Option { match self.recs.pop() { Some(rec) => Some(Record::with_data(self.names.clone(), rec.clone())), None => None, } } } #[derive(Clone, Debug)] pub struct Document { data: HashMap, } impl Document { fn new() -> Self { Self { data: HashMap::new(), } } pub fn add_field(&mut self, name: NT, field: CV) where CV: Into, NT: Into, { self.data.insert(name.into(), field.into()); } fn get_field(&self, name: NT) -> &CalcValue where NT: Into, { match self.data.get(&name.into()) { Some(data) => data, None => &CalcValue::None, } } #[allow(dead_code)] fn get_all(&self) -> Vec<(NameType, Field)> { let mut output = Vec::new(); for (key, value) in self.data.iter() { output.push((key.clone(), value.get(&Field::None))); } output } pub fn iter(&self) -> impl Iterator { self.data.iter() } } #[cfg(test)] mod documents { use super::*; use crate::name::Name; #[test] fn can_add_static_string() { let mut add = Document::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4().to_string(); add.add_field(name.clone(), data.clone()); let result = add.get_field(&name); match result { CalcValue::Value(holder) => match holder { Field::StaticString(result) => assert_eq!(result, &data), _ => unreachable!("got {:?}: should have received static string", holder), }, _ => unreachable!("got {:?}, should have been value", result), } } #[test] fn can_add_uuid() { let mut add = Document::new(); let name = Name::english(Uuid::new_v4().to_string().as_str()); let data = Uuid::new_v4(); add.add_field(name.clone(), data.clone()); let result = add.get_field(&name); match result { CalcValue::Value(holder) => match holder { Field::Uuid(result) => assert_eq!(result, &data), _ => unreachable!("got {:?}: should have received static string", holder), }, _ => unreachable!("got {:?}, should have been value", result), } } } #[derive(Clone, Debug)] pub struct Delete { query: Query, } impl Delete { pub fn new(query: Query) -> Self { Self { query: query.into(), } } pub fn get_query(&self) -> &Query { &self.query } } #[derive(Clone, Debug)] pub struct Update { query: Query, values: Document, } impl Update { pub fn new(query: Query) -> Self { Self { query: query.into(), values: Document::new(), } } pub fn get_query(&self) -> &Query { &self.query } pub fn get_values(&self) -> &Document { &self.values } pub fn get_values_mut(&mut self) -> &mut Document { &mut self.values } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Oid { oid: Uuid, } impl Oid { pub fn new() -> Self { Self { oid: Uuid::new_v4(), } } } #[cfg(test)] mod oids { use super::*; #[test] fn are_oids_random() { let count = 10; let mut holder: Vec = Vec::new(); while holder.len() < count { let result = Oid::new(); assert!(!holder.contains(&result)); holder.push(result); } } }